python筆記--3--函數、生成器、裝飾器、函數嵌套定義、函數柯裏化
函數
函數定義語法:
def 函數名([參數列表]):
‘‘‘註釋‘‘‘
函數體
函數形參不需要聲明其類型,也不需要指定函數返回值類型
即使該函數不需要接收任何參數,也必須保留一對空的圓括號
括號後面的冒號必不可少
函數體相對於def關鍵字必須保持一定的空格縮進
Python允許嵌套定義函數
在定義函數時,開頭部分的註釋並不是必需的,但是如果為函數的定義加上這段註釋的話,可以為用戶提供友好的提示和使用幫助。
Python是一種高級動態編程語言,變量類型是隨時可以改變的。Python中的函數和自定義對象的成員也是可以隨時發生改變的,可以為函數和自定義對象動態增加新成員。
lambda表達式可以用來聲明匿名函數,也就是沒有函數名字的臨時使用的小函數,尤其適合需要一個函數作為另一個函數參數的場合。
f = lambda x, z, y: x+y+z # 三個參數 print(f(3, 4, 5)) # 12 g = lambda x, y=2, z=3: x+y+z # 一個參數,另兩個有缺省參數 print(g(1)) # 6 print(g(1, z=4, y=5)) # 10 L = [(lambda x: x**2), (lambda x: x**3), (lambda x: x**4)] # lambda表達式作為列表元素 print(L[0](2), L[1](2), L[2](2)) # 4 8 16 D = {‘f1‘: (lambda: 2+3), ‘f2‘: (lambda: 2*3), ‘f3‘: (lambda: 2**3)} # lambda表達式作為字典的值 print(D[‘f1‘](), D[‘f2‘](), D[‘f3‘]()) # 5 6 8 L = [1, 2, 3, 4, 5] print(list(map(lambda x: x+10, L))) # 模擬向量運算 # [11, 12, 13, 14, 15]
內置函數map()可以將一個函數作用到一個序列或叠代器對象上。
print(list(map(str, range(5)))) # [‘0‘, ‘1‘, ‘2‘, ‘3‘, ‘4‘]def add5(n): return n+5 print(list(map(add5, range(10)))) # [5, 6, 7, 8, 9, 10, 11, 12, 13, 14] def add(x, y): return x+y print(list(map(add, range(5), range(5)))) # [0, 2, 4, 6, 8]
標準庫functools中的reduce()函數可以將一個接受2個參數的函數以累積的方式從左到右依次作用到一個序列或叠代器對象的所有元素上。
from functools import reduce seq = [1, 2, 3, 4, 5, 6, 7, 8, 9] print(reduce(lambda x, y: x+y, seq)) # 45 def add(x, y): return x+y print(reduce(add, range(10))) # 45 print(reduce(add, map(str, range(10)))) # 0123456789
內置函數filter將一個函數作用到一個序列上,返回該序列中使得該函數返回值為True的那些元素組成的filter對象。
seq = [‘foo‘, ‘x41‘, ‘?!‘, ‘***‘] def func(x): return x.isalnum() print(list(filter(func, seq))) # [‘foo‘, ‘x41‘]
生成器
包含yield語句的函數可以用來創建生成器對象,這樣的函數也稱生成器函數。yield語句與return語句的作用相似,都是用來從函數中返回值。與return語句不同的是,return語句一旦執行會立刻結束函數的運行,而每次執行到yield語句並返回一個值之後會暫停或掛起後面代碼的執行,下次通過生成器對象的__next__()方法、內置函數next()、for循環遍歷生成器對象元素或其他方式顯式“索要”數據時恢復執行。生成器具有惰性求值的特點,適合大數據處理。
Python使用生成器對延遲操作提供了支持。所謂延遲操作,是指在需要的時候才產生結果,而不是立即產生結果。這也是生成器的主要好處。
python有兩種不同的方式提供生成器:
生成器函數:
常規函數定義,但是,使用yield語句而不是return語句返回結果。yield語句一次返回一個結果,在每個結果中間,掛起函數的狀態,以便下次重它離開的地方繼續執行
生成器表達式:
類似於列表推導,但是,生成器返回按需產生結果的一個對象,而不是一次構建一個結果列表。
註意,生成器只能遍歷一次,再次遍歷生成器將不會有任何記錄。
def fib(n): ‘‘‘ 普通函數 ‘‘‘ a, b = 0, 1 while a < n: print(a, end=‘ ‘) a, b = b, a+b fib(100) # 0 1 1 2 3 5 8 13 21 34 55 89 def f(): ‘‘‘ 生成器函數 ‘‘‘ a, b = 0, 1 while True: yield a a, b = b, a+b a = f() for i in range(20): print(a.__next__(), end=‘ ‘) # 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 def gen(): yield 1 yield 2 yield 3 x, y, z = gen() print(x, y, z) # 1 2 3
函數嵌套定義
在Python中,函數是可以嵌套定義的。
def myMap(iterable, op, value): # 自定義函數 if op not in ‘+-*/‘: return ‘Error operator‘ def nested(item): # 嵌套定義函數 return eval(str(item)+op+str(value)) return map(nested, iterable) # 使用在函數內部定義的函數 print(list(myMap(range(5), ‘+‘, 5))) # 調用外部函數,不需要關心其內部實現 # [5, 6, 7, 8, 9] print(list(myMap(range(5), ‘-‘, 5))) # [-5, -4, -3, -2, -1] print(list(myMap(range(5), ‘*‘, 5))) # [0, 5, 10, 15, 20] print(list(myMap(range(5), ‘/‘, 5))) # [0.0, 0.2, 0.4, 0.6, 0.8]
可以使用嵌套函數定義可調用對象。
任何包含__call__()方法的類的對象都是可調用的。
def linear(a, b): def result(x): return a * x + b return result class Linear: def __init__(self, a, b): self.a, self.b = a, b def __call__(self, x): return self.a * x + self.b taxes = linear(0.3, 2) # 函數嵌套方式 print(taxes(5)) # 3.5 taxes = Linear(0.3, 2) # 類方式 print(taxes(5)) # 3.5
裝飾器
裝飾器(decorator)是函數嵌套定義的另一個重要應用。裝飾器本質上也是一個函數,只不過這個函數接收其他函數作為參數並對其進行一定的改造之後返回新函數。類中的靜態方法、類方法、屬性等也都是通過修飾器實現的,Python中還有很多這樣的用法。
def check_permission(func): def wrapper(*args, **kwargs): if kwargs.get(‘username‘) != ‘admin‘: raise Exception(‘Sorry. You are not allowed.‘) return func(*args, **kwargs) return wrapper class ReadWriteFile(object): # 把函數check_permission作為裝飾器使用 @check_permission def read(self, username, filename): return open(filename, ‘r‘).read() def write(self, username, filename, content): open(filename, ‘a+‘).write(content) # 把函數check_permission作為普通函數使用 write = check_permission(write) t = ReadWriteFile() print(‘Originally.......‘) print(t.read(username=‘admin‘, filename=r‘e:/test/a123.txt‘)) print(‘Now, try to write to a file........‘) t.write(username=‘admin‘, filename=r‘e:/test/a123.txt‘, content=‘\nhello world‘) print(‘After calling to write...........‘) print(t.read(username=‘admin‘, filename=r‘e:/test/a123.txt‘)) ‘‘‘ Originally....... 原始文本 Now, try to write to a file........ After calling to write........... 原始文本 hello world ‘‘‘
函數柯裏化
在計算機科學中,柯裏化(Currying)是把接受多個參數的函數變換成接受一個單一參數(最初函數的第一個參數)的函數,並且返回接受余下的參數且返回結果的新函數的技術。
偏函數
偏函數(partial function)和函數柯裏化(function currying)是函數式編程中常用的技術。有時候我們在復用已有函數時可能需要固定其中的部分參數,這除了可以通過默認值參數來實現之外,還可以使用偏函數。
也可以使用標準庫functools提供的partial()方法創建指定函數的偏函數。
from functools import partial def add3(a, b, c): return a+b+c def add2(a, c): return add3(a, 666, c) print(add2(1, 2)) # 669 add2 = partial(add3, b=666) # 使用標準庫functools提供的partial()方法創建指定函數的偏函數。 print(add2(a=1, c=2)) # 669
單參函數實現多參函數
函數柯裏化除了可以實現偏函數類似的功能之外,還可以利用單參數函數來實現多參數函數,這要歸功於Python對函數嵌套定義和lambda表達式的支持。
def func(a): # lambda表達式實現 return lambda b: a+b print(func(3)(5)) # 8 def func(a): # 函數嵌套定義實現 def funcNested(b): return a+b return funcNested print(func(3)(5)) # 8 def func(a): # 多級函數嵌套實現多參數要求 def funcNested(b): def funcNestedNested(c): return a+b+c return funcNestedNested return funcNested print(func(3)(5)(8)) # 16
python筆記--3--函數、生成器、裝飾器、函數嵌套定義、函數柯裏化