Python基礎復習函數篇
目錄
1.猴子補丁
2. global和nonlocal關鍵字
3.叠代器和生成器
4.遞歸函數
5.高階函數和lamdba函數
6.閉包
7.裝飾器
1. 猴子補丁
猴子補丁主要用於在不修改已有代碼情況下修改其功能或增加新功能的支持。
例如: 在使用第三方模塊時,模塊中的某些方法可能無法滿足我們的開發需求。此時,我們可以在不修改這些方法代碼的情況下,通過猴子補丁用一些
自己編寫的新方法進行替代,從而實現一些新的功能。
如很多代碼用到 import json,後來發現ujson性能更高,如果覺得把每個文件的import json 改成 import ujson as json成本較高,或者說想測試一下用ujson替換json是否符合預期,只需要在入口加上:
import json import ujson def monkey_patch_json(): json.__name__ = ‘ujson‘ json.dump = ujson.dumps json.loads = ujson.loads monkey_patch_json()
2. global和nonlocal關鍵字
在一個函數中使用global關鍵字,可以聲明在該函數中使用的是全局變量
而非局部變量。
在一個函數中要修改全局變量,必須使用global聲明。
在python中,函數的定義可以嵌套,即在一個函數的函數體中可以包含另一個函數的定義。
通過nonlocal關鍵字,可以使內層的函數直接使用外層函數中定義的變量。
例如:
def outer1(): # 不加nonlocal x = 10 # 局部變量 def inner(): x = 20 print(x) print(x) outer1() 輸出結果: 20 10 def outer2(): # 加nonlocal x = 10 # 局部變量 def inner(): nonlocal x= 20 print(x) print(x) outer2() 輸出結果: 20 20
3.叠代器和生成器
叠代器 Iterator
叠代器是訪問可叠代對象的工具。叠代器有兩個基本的方法:iter() 和 next()。
叠代器是指用iter(obj) 返回的對象(實例)。叠代器可以用next(it) 獲取可叠代對象的數據。
iter(iterable) 從可叠代對象中返回一個叠代器,其中iterable必須是一個能提供一個叠代器的對象,如字符串,列表或元組對象都可用於創建叠代器。
next(iterator) 從叠代器iterator中獲取下一個數據,如果無法獲取下一行數據,則觸發StopIteration異常
說明:
代器對象從集合的第一個元素開始訪問,直到所有的元素被訪問完結束。叠代器只能往前不會後退。
用iter函數可以返回一個可叠代對象的叠代器
L = [2, 3, 5, 7] it = iter(L) # it 綁定的是可叠代對象 L 提供的叠代器 print(next(it)) # 2 # 向叠代器要數據 print(next(it)) # 3 print(next(it)) # 5 print(next(it)) # 7 print(next(it)) # StopIteration異常 myit = iter(range(1, 10, 3)) # range也可以作為一個叠代器對象 1, 4, 7 print(next(myit)) # 1 print(next(myit)) # 4 print(next(myit)) # 7 print(next(myit)) # StopIteration異常 可用try捕獲
叠代器對象也可以使用常規for語句進行遍歷:
list=[1,2,3,4] it = iter(list) # 創建叠代器對象 for x in it: print (x, end=" ") 輸出結果如下: 1 2 3 4
生成器 generator
含有yield語句的函數是生成器函數,此函數被調用將返回一個生成器對象,在生成器函數調用return會觸發一個StopIteration異常(即生成結束)
生成器函數調用時返回的生成器是一次性的,當生成結束後將不再提供數據
yield 可翻譯為產生或生成
yield 語句
語法:
yield 表達式
說明:
yield 用於
def 函數中,目的是將此函數作為生成器函數使用
yield 用來生成數據,提供叠代器 next(it) 函數使用
下面舉例 自己用生成器實現python內建函數 filter
這個函數也可以加深對叠代器的理解:
def myfilter(fn, iterable): it = iter(iterable) # 先拿到叠代器 while True: try: x = next(it) # 取值 if fn(x): yield x except StopIteration: return # 生成結束 for x in myfilter(lambda y: y % 2 == 1, range(10)): print(x) # 1 3 5 7 9
iter(可叠代對象) 用可叠代對象生成叠代器
next(iter(可叠代對象)) 從叠代器中取值
yield x 包含yield的函數即生成器函數
4. 遞歸函數
遞歸函數是指在一個函數內部通過調用自己來完成一個問題的求解
當我們在進行問題的分解時,發現分解之後待解決的子問題與原問題有著相同
的特性和解法,只是在問題規模上與原問題相比有所減小,此時,就可以設計遞歸
函數進行求解
比如,對於計算n!的問題,可以將其分解為: n! = n * (n – 1) .可見,分解後的子問題
(n - 1)! 與原問題的 n! 的計算方法完全一樣,只是規模有所減少。
同樣,(n - 1)! 這個子問題有可以進一步分解為(n-1) * (n-2)! 可以進一步分解為
(n - 2)(n - 3)! ... , 直到要計算 1! 時,直接返回1 .
def fac(n): if n == 1: # 遞歸特性一:必須有一個明確的結束條件 return 1 else: return n * fac(n-1) # 遞歸特性二:每次遞歸都是為了讓問題規模變小,即找出函數的等價關系式 print(fac(5)) # 120 #遞歸特性三:遞歸層次過多會導致棧溢出,且效率不高
註意:
當問題規模較大時,遞歸調用會涉及到很多層的函數調用,一方面會由於棧操作影響程序運行速度,另一方面在Python中有棧的限制,
太多層的函數調用會引起棧溢出問題,如將 fac(5) 改成 fac(1000)則會報錯。
在定義遞歸函數時,要先明確函數的功能,這樣才能完成遞歸特性二的寫法。
5.高階函數和lamdba函數
高階函數是指把函數作為參數的一種函數
其中註意 函數不僅可以賦給形參 ,也可以賦給變量。賦值後,既可以用變量名替代函數名完成函數調用,
此時變量儲存的是函數的內存地址,通過()調用這個地址的內容,即函數。
lambda函數也稱為匿名函數, 是一種不使用def定義函數的形式,其作用是能快速定義一個簡短的函數
lambda函數的函數體只是一個表達式, 所以lambda函數通常只能實現比較簡單的功能。
形式 :
lamdba[參數1[,參數2,...,參數n]] : 表達式
冒號後面的表達式的計算結果即為該lamdba函數的返回值。實例如下:
1 def FunAdd(f,x,y): # 定義函數FunAdd 2 return f(x) + f(y) # 用傳給f的函數先對x和y分別處理後,再求和並返回 3 print(FunAdd(lambda x:x**2, 3, -5)) # 計算32 + (-5)2 4 print(FunAdd(lambda x:x**3,3,-5)) # 計算33 + (-5)3
也可以將lamdba函數賦值給一個變量,然後通過該變量去調用響應的lamdba函數。如:
fun = lamdba x:x**2 print(fun(3)) # 輸出9
其他高階函數
filter函數
filter(function or None, iterable) --> filter object
‘‘‘ Return an iterator yielding those items of iterable for which function(item)
is true. If function is None, return the items that are true.‘‘‘
>>>list(filter(None,[1,2,0,True,False]))
>>>Out: [1, 2, True]
zip函數
zip(iter1 [,iter2 [...]]) --> zip object
| Return a zip object whose .__next__() method returns a tuple where
| the i-th element comes from the i-th iterable argument. The .__next__()
| method continues until the shortest iterable in the argument sequence
| is exhausted and then it raises StopIteration.
參數iterable為可叠代的對象,並且可以有多個參數。該函數返回一個以元組為元素的列表,其中第 i 個元組包含每個參數序列的第 i 個元素。返回的列表長度被截斷為最短的參數序列的長度。只有一個序列參數時,它返回一個1元組的列表。沒有參數時,它返回一個空的列表。
a = [‘張三‘,‘小花‘,‘老王‘] b = [‘男‘,‘女‘,‘男‘] c = [‘已婚‘,‘已婚‘,‘未婚‘] z = zip(a,b,c) print(type(z)) # 註意:python3 不再返回列表,而是返回zip對象 print([x for x in z]) 輸出: <class ‘zip‘> [(‘張三‘, ‘男‘, ‘已婚‘), (‘小花‘, ‘女‘, ‘已婚‘), (‘老王‘, ‘男‘, ‘未婚‘)]
enumerate函數
enumerate(iterable, start=0)
返回一個enumerate對象此對象生成的類型為(索引,值)的元組,默認索引從零開始,也可以用第二個參數start指定
- iterable -- 一個序列、叠代器或其他支持叠代對象。
- start -- 下標起始位置
例:
day = [‘None‘,‘Monday‘,‘Tuesday‘,‘Wednesday‘,‘Thursday‘,‘Friday‘,‘Saturday‘,‘Sunday‘] for i in enumerate(day): print(‘星期{} :{}‘.format(*i)) 輸出: 星期0 :None 星期1 :Monday 星期2 :Tuesday 星期3 :Wednesday 星期4 :Thursday 星期5 :Friday 星期6 :Saturday 星期7 :Sunday
6.閉包
如果內層函數使用了外層函數中定義的局部變量,並且外層函數的返回值是內層函數的引用,就構成了閉包
其中定義在外層函數中但由內層函數使用的變量被稱為自由變量。
一般情況下,如果一個函數結束,那麽該函數中定義的局部變量就都會被釋放。
然而閉包是一種特殊情況, 外層函數在結束時會發現其定義的局部變量將來會在內層函數中使用,此時
外層函數就會把這些自由變量綁定到內層函數。
因此所謂閉包, 實際上就是將內層函數的代碼以及自由變量(外層函數定義,但會由內層函數使用)打包在一起。
def outer(x): y = 10 # 定義局部變量y並賦值為10 def inner(z): # 在out函數中定義嵌套函數inner nonlocal x,y return x + y + z return inner # 返回嵌套函數inner的引用 f = outer(5) # 將返回的inner函數賦給f g = outer(50) # 將返回的inner函數賦給g print(‘f(20)的值為:‘,f(20)) print(‘g(20)的值為:‘,g(20)) print(‘f(30)的值為:‘,f(30)) print(‘g(20)的值為:‘,g(30)) 輸出結果: f(20)的值為: 35 g(20)的值為: 80 f(30)的值為: 45 g(20)的值為: 90
提示:
閉包的主要作用在於可以封存函數執行的上下文環境。
比如,上例通過兩次調用outer函數形成了兩個閉包 f 和 g 。這兩個閉包具有相互獨立的上下文
環境(一個閉包(f)中x = 5,y = 10,另一個閉包中x = 50,y = 10),且每個閉包可多次調用。
7.裝飾器(Decorator)
利用裝飾器,可以在不修改已有函數的情況下向已有函數中註入代碼,使其具備新的功能。
比如利用裝飾器可以將日誌處理,執行事件計算,性能測試,事務處理等較為通用且與函數功能本身無關的代碼註入到不同的函數中,從而使得代碼更加簡潔。
一個裝飾器可以為多個函數註入代碼, 一個函數也可以註入多個裝飾器的代碼
本質上,裝飾器就是一個返回函數的高階函數。
實例1:
def deco(func): # 定義裝飾器函數deco def inner(*args, **kwargs): print(‘deco begin‘) func(*args, **kwargs) print(‘deco end‘) return inner # 返回函數inner的引用 @deco #語法糖,相當於在f函數執行前執行了f = deco(f) def f(a,b,c): print(‘a+b+c =‘,a+b+c) f(1,2,3)
輸出結果: deco begin a+b+c = 6 deco end
7.2 帶參數和有返回值的裝飾器
裝飾器還有更大的靈活性,例如帶參數的裝飾器,在上面的裝飾器調用中,裝飾器函數的參數就是業務函數 f。
裝飾器的語法允許我們在調用時,提供其他參數,比如@deco(a) 。
這樣就Wie裝飾器的編寫和使用提供了更大的靈活性。比如,我們可以在裝飾器中指定日誌的等級,因為不同業務
函數可能需要的日誌等級是不一樣的。
另外有些業務函數有自己的return值,所以我們在裝飾器函數中也要設定返回值,即將業務函數的返回值在裝飾函數中再返還給業務函數。示例如下:
1 import logging 2 3 def use_deco(level): # 這個函數將裝飾器參數傳到內部環境中 4 def deco(func): # 真正的裝飾器函數 5 def wrapper(*args, **kwargs): #內部用來實現裝飾功能的函數 6 if level == ‘warn‘: 7 logging.warning("%s is running" % func.__name__) # 打印一個警告信息; 每個函數對象都有一個__name__屬性,可以拿到函數的名字 8 elif level == "info": 9 logging.info("%s is running" % func.__name__) # 默認日誌級別是warning,所以info不會被打印 10 return func(*args) # 返回業務函數的返回 11 return wrapper # 返回函數wrapper的引用 12 return deco # 返回裝飾器函數的引用 13 14 @use_deco(level=‘warn‘) #語法糖,還是相當於@deco(f = deco(f)),因為use_deco(level=‘warn‘) 返回的是deco 15 def f(a,b,c): 16 print(‘a+b+c =‘,a+b+c) 17 return a * b * c 18 19 a = f(1,2,3) 20 print(‘a*b*c =‘,a) 21 22 運行結果: 23 a+b+c = 6 24 WARNING:root:f is running 25 a*b*c = 6
裝飾器順序
一個函數還可以同時定義多個裝飾器,比如:
@a @b @c def f(): pass
他的執行順序是從裏到外,最先調用最裏層的裝飾器,最後調用最外層的裝飾器,它等效於
f = a ( b ( c ( f ) ) )
Python基礎復習函數篇