python裝飾器詳解
Python之裝飾器詳解
一、裝飾器定義
定義一個函數(shù),可以接受一個函數(shù)作為參數(shù),對該函數(shù)進行一些包裝,不改變函數(shù)的本身。
二、裝飾器四部曲(分解)
1、函數(shù)可賦值給變量。若賦值給變量的是調(diào)用后的函數(shù),變量的值就是return的返回值。
切記:函數(shù)賦值給變量,只看return的值。分清楚函數(shù)是處于調(diào)用狀態(tài)還是未被調(diào)用狀態(tài)。若函數(shù)沒有寫return,默認return為None。
例如:
解釋:把函數(shù)foo賦值給a和b,a賦值的是調(diào)用后的函數(shù),變量的值就是返回值。b賦值的是調(diào)用前的函數(shù),所以b就是那個賦值的函數(shù)。函數(shù)本身+(),就是調(diào)用。
可以用callable判斷某個東西是否可以被調(diào)用,call是調(diào)用的意思,able是“能的”形容詞后綴,翻譯就是可調(diào)用的。b是函數(shù)本身,可以被調(diào)用,a是函數(shù)的結(jié)果,不能被調(diào)用。
小技巧:
我們可以用.name看一個函數(shù)的信息,例如:b函數(shù)其實就是‘foo’函數(shù)。
2、函數(shù)可以作為參數(shù)傳遞,在函數(shù)內(nèi)部仍可進行調(diào)用。可以將函數(shù)在內(nèi)部定義理解為一個變量在內(nèi)部定義。
解釋:我先定義一個函數(shù)foo,然后再定義一個函數(shù)bar,我調(diào)用foo的時候,即foo(),沒有設置傳遞參數(shù),打印了1234.我調(diào)用bar的時候,傳遞了一個函數(shù)foo作為參數(shù),即bar(foo),得到的結(jié)果就是func(),調(diào)用函數(shù)本身,所以得到1234的結(jié)果。
3、函數(shù)可嵌套定義,并可在內(nèi)部直接調(diào)用。且可調(diào)用外層函數(shù)傳遞的參數(shù)。這一步很關鍵。
解釋:內(nèi)層函數(shù)可以調(diào)用外層函數(shù)傳遞的參數(shù),f(1234)傳遞給了內(nèi)層函數(shù)。內(nèi)層沒有找到x變量的值,就要去臨近的外層變量尋找。外層傳遞一個1234,就傳遞給內(nèi)層了。最后一行的bar()是在外層函數(shù)的內(nèi)部直接調(diào)用內(nèi)層函數(shù)。
函數(shù)嵌套定義就是可以把函數(shù)看成定義一個變量,類似于b=1,還有就是內(nèi)層函數(shù)可以調(diào)用外層函數(shù)傳遞的參數(shù),記住這兩點就可以了。
4、函數(shù)可以作為return的返回值
解釋:上面定義了一個函數(shù),下面定義了一個函數(shù)返回函數(shù)。對變量a進行賦值,可以使用a.__name__看到a的函數(shù)名,a就是bar,為什么是bar呢,因為第一條:給變量a賦值的時候,只看return,return的是函數(shù)的函數(shù)func,所以是bar。
三、組合起來就是裝飾器
解釋:最后的foo就是wrapper,就是deco(foo),是內(nèi)層函數(shù)“g()”,可以用foo.name查看,就是wrapper,callable(foo),可以看到它可以調(diào)用。
還有一點,如下圖,筆者在學習過程中碰到的一點疑問:
上面的三個過程,第一個是給變量賦值的過程,第二個是打印變量的過程,第三個是函數(shù)調(diào)用的過程。變量賦值看return,不要看他出現(xiàn)什么結(jié)果,這個時候就是把return的1賦值給a,所以打印a才會出來1,函數(shù)調(diào)用的過程,就是執(zhí)行函數(shù)內(nèi)部程序的過程,函數(shù)內(nèi)部有一個打印,一個return,所以才會出現(xiàn)這樣的結(jié)果。
這樣,我們就很好理解上面的結(jié)果了。
上面的這個“函數(shù)賦值給變量”就是裝飾器的核心原理,裝飾器接受函數(shù)作為參數(shù)完成調(diào)用,再將返回結(jié)果賦值給該函數(shù)同名的變量。以后再通過該變量名調(diào)用,就是被裝飾器裝飾過的函數(shù)。
四、Python裝飾器用法:@
解釋:@deco相當于foo=deco(foo)。
1、內(nèi)層函數(shù)也可以定義參數(shù):
解釋:參數(shù)的傳遞是先傳給wrapper,wrapper在把這個參數(shù)傳遞給func進行調(diào)用。wrapper接受的參數(shù)的個數(shù)要跟foo的一樣,我要通過wrapper轉(zhuǎn)給foo。
2、裝飾器方法總結(jié):
通常最內(nèi)層函數(shù)的倒數(shù)第二層函數(shù),定義接受一個函數(shù)參數(shù)
裝飾器內(nèi)部嵌套定義了什么函數(shù),就要講該函數(shù)作為return返回值,未調(diào)用的。
最內(nèi)層函數(shù)的參數(shù)定義最好和傳人的參數(shù)定義一致,或者使用(*arg,*kwargs)這種可變參數(shù)的定義方式。
裝飾器外層函數(shù)只會執(zhí)行一次。外層的操作執(zhí)行完成后,就不會再輸出了。執(zhí)行的都是內(nèi)層函數(shù)wrapper。
對裝飾的函數(shù)進行屬性傳遞:被裝飾的函數(shù)的name,名稱,doc注釋等等,都無法保留,都是內(nèi)層函數(shù)的,這個時候就要把wrapper的屬性改掉,改成我們接受函數(shù)的屬性。也有一個專門的模塊:from functools import wraps。用法@wraps(func)
nonlocal:Python可以使用外部函數(shù)變量,無法修改外部函數(shù)自身。此時可以用nonlocal關鍵字標記需要修改的變量就可以修改該變量的值了。例如統(tǒng)計函數(shù)的次數(shù):
帶參數(shù)的裝飾器:無非就是再加一層,內(nèi)部可以不用動。如下,增加一個參數(shù)123.