python 函數、裝飾器、迭代器、生成器、列表生成式

函數

函數的參數

位置參數

def foo(x,y,z):#位置形參:必須被傳值的參數
    print(x,y,z)

# foo(1,2,3)
foo(1,2,3) #位置實參數:與形參一一對應

默認參數(默認參數必須指向不變對象)

def register(name,age,sex='male'): #形參:默認參數
    print(name,age,sex)

register('asb',age=40)
register('a1sb',39)
register('a2sb',30)
register('a3sb',29)

register('鋼蛋',20,'female')
register('鋼蛋',sex='female',age=19)

默認參數須要注意的問題

(1):默認參數必須跟在非默認參數後閉包

def register(sex='male',name,age): #在定義階段就會報錯
    print(name,age,sex)

(2):默認參數在定義階段就已經賦值了,並且只在定義階段賦值一次app

a=100000000
def foo(x,y=a):
    print(x,y)
a=0
foo(1)

(3):默認參數的值一般定義成不可變類型函數

可變長參數

def foo(x,y,*args): #*會把溢出的按位置定義的實參都接收,以元組的形式賦值給args
    print(x,y)
    print(args)

foo(1,2,3,4,5)


def add(*args):
    res=0
    for i in args:
        res+=i
    return res
print(add(1,2,3,4))
print(add(1,2))



def foo(x, y, **kwargs):  # **會把溢出的按關鍵字定義的實參都接收,以字典的形式賦值給kwargs
    print(x, y)
    print(kwargs)
foo(1,2,a=1,name='egon',age=18)


def foo(name,age,**kwargs):
    print(name,age)
    if 'sex' in kwargs:
        print(kwargs['sex'])
    if 'height' in kwargs:
        print(kwargs['height'])

foo('egon',18,sex='male',height='185')
foo('egon',18,sex='male')


def calc(*numbers):
    sum = 0
    for n in numbers:
        sum = sum + n * n
    print(sum)
num = [1,2,3]       #num位置能夠是list或tuple
calc(*num)
*nums表示把nums這個list的全部元素做爲可變參數傳進去。這種寫法至關有用,並且很常見。

關鍵字參數

def foo(x,y,z):
    print(x,y,z)

foo(z=3,x=1,y=2)

關鍵字參數須要注意的問題:工具

1:關鍵字實參必須在位置實參後面
2: 不能重複對一個形參數傳值
foo(1,z=3,y=2) #正確
foo(x=1,2,z=3) #錯誤
foo(1,x=1,y=2,z=3) #錯誤

命名關鍵字參數(*號能夠把它後面的關鍵詞過濾掉)

def foo(name,age,*,sex='male',height):
    print(name,age)
    print(sex)
    print(height)
#*後定義的參數爲命名關鍵字參數,這類參數,必須被傳值,並且必須以關鍵字實參的形式去傳值

foo('egon',17,height='185')
def per(name,age,*,city):
    print(name,age,city)
per('make',24,city='beijing')
def per(name,age,*,city='beijing',job):
    print(name,age,city,job)
per('make',24,job='it')

總結:

*args是可變參數,args接收的是一個tuple;
**kw是關鍵字參數,kw接收的是一個dict

---

函數對象

返回值能夠是函數

def foo():
    print('from foo')

def bar(func):
    #print(func)
    return func
f = bar(foo)  #取到返回值foo內存地址
print(f)
f()         #調用

能夠看成參數傳遞

def foo():
  print('from foo')
def bar(func):
  print(func)
  func()

bar(foo) # foo函數內存地址被看成參數傳遞到bar函數中,並調用
  
 '''
 結果:
 <function foo at 0x00000049CC9A3E18>
 from foo
 '''

函數嵌套

函數嵌套是閉包函數基礎測試

函數嵌套定義

函數的嵌套定義: 顧名思義就是函數裏面, 套函數。應用如閉包、裝飾器
def f1():
    def f2():
        print('from f2')
        def f3():
            print('from f3')
        f3()
    f2()
f1()

函數的嵌套調用

函數的嵌套調用:屬於面向過程(分子原子級操做),細分問題
def max2(x,y):
    """判斷兩個數的大小"""
    return x if x>y else y


def max4(a,b,c,d):
    """判斷4個數的大小"""
    res1 = max2(a,b)
    res2 = max2(res1,c)
    res3 = max2(res2,d)
    return res3

print(max4(12,34,11,35))

名稱空間和做用域

名稱空間定義

#倒入模塊
import time
#變量賦值
name = 'engon'
#函數定義
def func():
    pass
#類定義
class Foo:
    pass

名稱空間分類

內置名稱空間:隨着python解釋器的啓動而產生

print(max)
print(min)

#全部的內置空間
#dir 對象的內建名稱空間
import builtins
for i in dir(builtins):
    print(i)

全局名稱空間

文件的執行會產生全局名稱空間,指的是文件級別定義的名字都會放入改空間

局部名稱空間

調用函數時會產生局部名稱空間,只在函數調用時臨時綁定,調用結束解綁定

做用域

爲名稱空間的具體應用。他們之間的關係,以下對應:
1.全局做用域:內置名稱空間,全局名層空間
2.局部做用:局部名稱空間

做用於的優先級順序:局部名稱空間---》全局名層空間---》內置名稱空間ui

查看全局做用域內的名字:gloabls()url

查看局部做用域內的名字:locals()code


閉包函數

若是在一個內部函數裏,對在外部做用域(但不是在全局做用域)的變量進行引用,那麼內部函數就被認爲是閉包(closure)。一個閉包就是你調用了一個函數A,這個函數A返回了一個函數B給你。這個返回的函數B就叫作閉包。你在調用函數A的時候傳遞的參數就是自由變量對象

函數嵌套的一種方式,必須遵照如下規則:
a. 定義在內部函數 b. 包含對外部做用域而非全局做用域的引用,該內部函數就成爲閉包函數
# 定義實例,對象隱藏,全局不可見
def f1():
# x內部隱藏,全局不可見
    x = 1
    def f2():
        print(x)
    return f2
f=f1()
#print(f)
# x由於隱藏,因此全局做用域無效
x=100000000000000000000000000
f()

#下面也是個閉包
#!/usr/bin/python
# -*- coding:utf-8 -*-
def f1():
    x = 1
    y = 2
    def f2():
        print(x,y)
    return f2
a = f1()

print(a)
print(a.__closure__[0].cell_contents)

閉包應用:惰性計算

from urllib.request import urlopen
#res = urlopen('http://crm.oldboyedu.com').read()
#print(res.decode('utf-8'))

def index(url):
    def get():
        return urlopen(url).read()
    return get
# 存在內存裏
oldboy = index('http://crm.oldboyedu.com')
#print(oldboy().decode('utf-8'))
# 閉包函數相對與普通函數會多出一個__closure__的屬性,裏面定義了一個元組用於存放全部的cell對象,
# 每一個cell對象一一保存了這個閉包中全部的外部變量
print(oldboy.__closure__[0].cell_contents)

-----------------------------------------------

內置函數

數學運算 abs(),round(),pow(),divmod(),max(),min(),sum()

abs(-5)  # 取絕對值,也就是5
round(2.623423,5)  # 不帶第二個參數(四捨五入取整,也就是3.0, 4爲精準到四位四捨五入),不然第二個參數爲小數點後面的位數
pow(2, 4)  # 至關於2**3,若是是pow(2, 3, 5),至關於2**3 % 5
divmod(9, 2)  # 返回除法結果和餘數
max([1, 5, 2, 9])  # 求最大值
min([9, 2, -4, 2])  # 求最小值
sum([2, -1, 9, 12])  # 求和

工廠函數 int(), float(), str(), bool(),slice(),list(),tuple(), dict(), set(), frozenset()

int("5")  # 轉換爲整數 integer
float(2)  # 轉換爲浮點數 float
str(2.3)  # 轉換爲字符串 string
bool(0)   # 轉換爲相應的真假值,在Python中,0至關於False在Python中,下列對象都至關於False:[], (), {}, 0, None, 0.0, ''
slice(5, 2, -1)   # 構建下標對象 slice,切片函數
list((1, 2, 3))   # 轉換爲表 list
tuple([2, 3, 4])  # 轉換爲定值表 tuple
dict(a=1, b="hello", c=[1, 2, 3])   # 構建詞典 dictionary
set()           #建立集合函數
frozenset()     #建立一個不可修改的集合 如:s=frozenset({1,2}) # 定義不可變集合

類型轉換 ord(), chr(), bin(), hex(), oct(), complex()

ord('A')        # "A"字符對應的數值
chr(65)         # 數值65對應的字符
bin(56)         # 返回一個字符串,表示56的二進制數
hex(56)         # 返回一個字符串,表示56的十六進制數
oct(56)         # 返回一個字符串,表示56的八進制數
complex(3,9)    # 返回複數 3 + 9j

序列操做 all(), any(), sorted(), reversed()

all([True,1,"hello!"])      # 是否全部的元素都至關於True值
any(["",0,False,[],None])   # 是否有任意一個元素至關於True值
sorted([1,5,3])             # 返回正序的序列,也就是[1,3,5]
reversed([1,5,3])           # 返回反序的序列,也就是[3,5,1]

編譯執行函數 repr(), compile(), eval(), exec()

'''
參數mode是用來指明那種表示的源碼類型;
若是是exec類型,表示這是一個序列語句,能夠進行運行;
若是是eval類型,表示這是一個單一的表達式語句,能夠用來計算相應的值出來;
若是是single類型,表示這是一個單一語句,採用交互模式執行,在這種狀況下,若是是一個表達式,通常會輸出結果,而不是打印爲None輸出。
'''

a = repr('me')           # 返回一個對象的字符串表示。有時可使用這個函數來訪問操做。
print(a)

b = compile("print('Hello')",'','exec')     # 編譯字符串成爲code對象
exec(b)

d = "1+2+3+4*4"
e = compile(d,'','eval')             # 解釋字符串表達式。參數也能夠是compile()返回的code對象
print(eval(e))

b = compile("print('Hello')",'','eval')
eval(b)

幫助函數 dir(), help(), id(), len(), challables()

'''
dir()   不帶參數時返回當前範圍內的變量,方法和定義的類型列表,帶參數時返回參數的屬性,方法列表
help()  返回對象的幫助文檔
id()    返回對象的內存地址
len()   返回對象長度,參數能夠是序列類型(字符串,元組或列表)或映射類型(如字典)
challable()  判斷對象是否能夠被調用,能被調用的對象就是一個callables對象,好比函數和帶有__call__()的實例
'''
l = []
print(dir(l))
print(help(l))
x = 1
y = 2
print(id(x),id(y))
print(y is x)       #判斷的是身份

def func():
    pass
print(callable(func))

做用域查看函數 globals(), locals(), vars()

globals()  返回一個描述當前全局變量的字典
locals()   打印當前可用的局部變量的字典
vars()    1. 當函數不接收參數時,其功能和locals函數同樣,返回當前做用域內的局部變量。
          2. 當函數接收一個參數時,參數能夠是模塊、類、類實例,或者定義了__dict__屬性的對象。

-----------------------------------------------

裝飾器

裝飾器:修飾別人的工具,修飾添加功能,工具指的是函數 裝飾器自己能夠是任何可調用對象,被裝飾的對象也能夠是任意可調用對象

爲何要用裝飾器?

a. 開放封閉原則:對修改是封閉的,對擴展是開放的 
b. 裝飾器就是爲了在不修改被裝飾對象的源代碼以及調用方式的前提下,爲其添加新功能

1.裝飾器的基本寫法(可傳參數)

import time

def timmer(func):
    def wrapper(*args):
        # 在運行函數前執行,time.time()爲當前時間(格林威治時間1970到如今的秒數)
        start_time = time.time()
        # 被裝飾函數執行,沒有返回值,默認爲
        res = func(*args)
        # 在函數運行後執行
        stop_time = time.time()
        print('run time is %s' % (stop_time - start_time))
    return wrapper

@timmer
def ff(args):
    time.sleep(1)
    print('welecome to %s'% args)

ff('china')

2.多實例添加

import time

def timmer(func):
    def wrapper(*args,**kwargs):
        # 在運行函數前執行,time.time()爲當前時間(格林威治時間1970到如今的秒數)
        start_time = time.time()
        # 被裝飾函數執行,沒有返回值,默認爲
        res = func(*args,**kwargs)
        # 在函數運行後執行
        stop_time = time.time()
        print('run time is %s' % (stop_time - start_time))
    return wrapper

@timmer
def foo(ar,kw):
    time.sleep(1)
    print('name:%s \nage:%s'% (ar,kw))

@timmer
def ff(job):
    print('job %s' % job)


res = foo('mk','18')
res1 = ff('it')

3.多裝飾器,執行順序(不太明白)

import time

def timmer(func):
    def wrapper(*args):
        # 在運行函數前執行,time.time()爲當前時間(格林威治時間1970到如今的秒數)
        start_time = time.time()
        # 被裝飾函數執行,沒有返回值,默認爲
        res = func(*args)
        # 在函數運行後執行
        stop_time = time.time()
        print('run time is %s' % (stop_time - start_time))
    return wrapper

login_user = {'user':None,'status':False}
def auth(func):
    def wrapper(*args,**kwargs):
        if login_user['user'] and login_user['status']:
            res = func(*args,**kwargs)
            return res
        else:
            name = input('name:')
            passwd = input('passwd')
            if name == 'mk' and passwd == '123':
                login_user['user']='mk'
                login_user['status']='123'
                print('\033[45mlogin successful\033[0m')
                res = func(*args,**kwargs)
                return res
            else:
                print('\033[43mlogin err\033[0m')
    return wrapper


@auth
@timmer
def index():
    time.sleep(1)
    print('namenage')

index()

4.有參裝飾器

login_user = {'user':None,'status':False}
def auth(db_type):
    def auth2(func):
        def wrapper(*args,**kwargs):
            if db_type == 'file':
                if login_user['user'] and login_user['status']:
                    res = func(*args,**kwargs)
                    return res
                else:
                    name = input('name:')
                    passwd = input('passwd')
                    if name == 'mk' and passwd == '123':
                        login_user['user']='mk'
                        login_user['status']='123'
                        print('\033[45mlogin successful\033[0m')
                        res = func(*args,**kwargs)
                        return res
                    else:
                        print('\033[43mlogin err\033[0m')
            elif db_type == 'ldap':
                print('db_type-->ldap')
        return wrapper
    return auth2

@auth('file')  #傳參

def index():
    print('namenage')
index()

# @auth('ldap')
#
# def index():
#     print('namenage')
# index()

5.裝飾器@functools.wraps + 有無參裝飾器

a.官網裝飾器定義:

裝飾器是一個函數,其主要用途是包裝另外一個函數或類。這種包裝的首要目的是透明地修改或加強被包 裝對象的行爲。

b.@functools.wraps做用:

由於裝飾器是一個閉包函數,怎樣能獲取被修飾函數的詳細信息,如:__name__。      此類實際調用函數的屬性。@functools.wraps就提供了該功能。      它是如何實現的,下面的例子是經過反射的原理把func的一些屬性更新給了callf函數。原func的屬性      不變。可是callf的屬性仍是跟原func不徹底同樣,so,若有相似的問題,還須要本身寫。      具體,請看將來的類章節。

functool.wrpaps 保持了wrapper.__name__ = func.__name__

#!/usr/bin/python
# -*- coding:utf-8 -*-
import functools


def log(text):          #定義裝飾器
    if isinstance(text,str):
        def decorator(func):
            @functools.wraps(func)
            def wrapper(*args,**kwargs):
                print('%s %s' % (text,func.__name__))
                return func(*args,**kwargs)
            return wrapper
        return decorator
    else:
        @functools.wraps(text)
        def wrapper(a,b):
            print('call %s' % text.__name__)
            return text(a,b)

        return wrapper

#情景一:裝飾器自己不用傳入參數,只接收函數func
@log          #調用裝飾器(將函數now當參數傳入log裝飾器)
def now(a,b): #能夠傳值
    print('20159',a,b)
#now()
a = now('c','d')

#情景二:裝飾器自己須要傳入參數,再接收函數func
@log('ii.py')
def now():
    print('2015')
#now()
#print(now.__name__)

----

迭代器

迭代器只不過是一個實現迭代器協議的容器對象。
可迭代的對象:內置__iter__方法的,都是可迭代的對象

特色:

一、訪問者不須要關心迭代器內部的結構,僅需經過next()方法不斷去取下一個內容
二、不能隨機訪問集合中的某個值 ,只能從頭至尾依次訪問
三、訪問到一半時不能往回退
四、便於循環比較大的數據集合,節省內存

可迭代對象經過__iter__轉成迭代器對象

a = iter([1,2,3,4,5])

print(a)
print(a.__next__())
print(a.__next__())
print(a.__next__())
print(a.__next__())
print(a.__next__())

結果:

<list_iterator object at 0x1028d95c0>
1
2
3
4
5

判斷一個對象是可迭代對象

經過collections模塊的Iterable類型判斷:
from collections import Iterable
print isinstance('abc', Iterable)   # str是否可迭代
True
print isinstance('1,2,3', Iterable) # list是否可迭代
True
print isinstance(123, Iterable)     # 整數是否可迭代
False

優勢

1.提供了一種不依賴下標的迭代方式

l = [122,343,55,6,7]
i = iter(l)
print(i)
print(next(i))
print(next(i))
'''結果
<list_iterator object at 0x1007d9518>
122
343
'''

2.就迭代器自己來講,更節省內存。迭代一個值,原來迭代的值丟棄

缺點

  1. 沒法獲取迭代器對象的長度
  2. 不如序列類型取值靈活,是一次性的,只能日後取值,不能往前退
l = [122,343,55,6,7]
i = iter(l)
for item in i:
    print(item)

print('='*10)
'''結果
122
343
55
6
7
==========
'''
for item in i:
    print(item)

f = open('a.txt','r',encoding='utf-8')
#print(next(f),end='')
for ii in f:
    #print(ii)
    print(ii.strip())
'''結果
222333
sdf
'''

-------------------------------

生成器

一個函數調用時返回一個迭代器,那這個函數就叫作生成器(generator);若是函數中包含yield語法,那這個函數就會變成生成器。

yield使用

def xran():
    print('one')
    yield 1
    print('two')
    yield 2
    print('three')
    yield 3
a = xran()
print(a.__next__())
print(a.__next__())
print(a.__next__())
#a.__next__()   #循環完畢拋出StopIteration
a.close     #關閉生成器

生成器表達式

a = [7,8,9]
b = (i**2 for i in a)

print(b)
print(next(b))
print(next(b))
print(next(b))
a. 至關於爲函數封裝好__iter__和__next__    
b. return只能返回一次值,函數就終止了,而yield能返回屢次值,每次返回都會將函數暫停,下一次next會從上一次暫停的位置繼續執行

yield程序實例(tail -f a.txt | grep 'python' 類功能python程序版)

import time


def tail(ttt):
    with open(ttt,encoding='utf-8') as f:
        f.seek(0,2)
        #print(f.tell())
        while True:
            line = f.readline().strip()
            if line:
                return line
                yield line
            else:
                time.sleep(0.2)

# 測試tail功能是否有效(用echo ‘python’>> a.txt,不能在pc中)
# t = tail('a.txt')
#print(next(t))
# for line in t:
#     print(line)


def grep(patten,lines):
    for line in lines:
        if patten in line:
            yield line
g = grep('python',tail('a.txt'))
print(g)
for i in g:
    print(i)

----------------------------------

列表生成式

#篩選出僅偶數的平方
a = [x*x for x in range(1,11) if x % 2 ==0]
print(a)          #[4, 16, 36, 64, 100]


b = [m+n for m in 'ABC' for n in 'abc']
print(b)          #['Aa', 'Ab', 'Ac', 'Ba', 'Bb', 'Bc', 'Ca', 'Cb', 'Cc']


import os
c = [d for d in os.listdir('.')] #遍歷當前路徑下的文件
print(c)            #print([d for d in os.listdir('.')])


d = {'x': 'A', 'y': 'B', 'z': 'C' }
for k,v in d.items():
    print(k,'=',v)
print([k + '=' + v for k,v in d.items()])


L = ['Hello', 'World', 'IBM', 'Apple']
e = [s.lower() for s in L]
print(e)


#內建的isinstance函數能夠判斷一個變量是否是字符串:
x='abc'
y=123
print(isinstance(x, str))
print(isinstance(y, str))

練習:

請修改列表生成式,經過添加if語句保證列表生成式能正確地執行
# L1 = ['Hello', 'World', 18, 'Apple', None]
# 期待輸出: ['hello', 'world', 'apple'] print(L2)

方法一:
L1 = ['Hello', 'World', 18, 'Apple', None]
L2 = []
for i in L1:
    if isinstance(i,str):
        L2.append(i.lower())
print(L2)
方法二:
L2 = [s.lower() for s in L1 if isinstance(s,str)]
print(L2)
相關文章
相關標籤/搜索