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