裝飾器基礎(chǔ)知識
裝飾器是可調(diào)用的對象葫督,其參數(shù)是另一個函數(shù)(被裝飾的函數(shù))。裝飾器可能會處理被裝飾的函數(shù)斜筐,然后把它返回冰啃,或者將其替換成另一個函數(shù)或可調(diào)用對象邓夕。
假如有個名為decorate的裝飾器:
@decorate
def target():
? ? print('running target()')
上述代碼的效果與下述寫法一樣:
def target():
? ? print('running target()')
target=decorate(target)
這兩種寫法的最終結(jié)果一樣:上述兩個代碼片段執(zhí)行完畢后得到的target不一定是原來那個target函數(shù),而是decorate(target)返回的函數(shù)亿笤。
python何時執(zhí)行裝飾器
裝飾器的一個關(guān)鍵特性是翎迁,它們在被裝飾的函數(shù)定義之后立即運行栋猖,這通常是在導(dǎo)入(即python加載模塊時)
上述腳本運行得到的輸出如下:
上述示例主要想強調(diào)净薛,函數(shù)裝飾器在導(dǎo)入模塊時立即執(zhí)行,而被裝飾的函數(shù)只在明確調(diào)用時運行蒲拉,考慮到裝飾器在真實代碼中的常用調(diào)用方式肃拜,上述示例有兩個不尋常的地方:
1、裝飾器函數(shù)與被裝飾的函數(shù)在同一個模塊中定義雌团。實際情況是燃领,裝飾器常在一個模塊中定義,然后應(yīng)用到其他模塊的函數(shù)中锦援。
2猛蔽、registry裝飾器返回的函數(shù)與通過參數(shù)傳入的相同。實際上灵寺,大多數(shù)裝飾器會在內(nèi)部定義一個函數(shù)曼库,然后將其返回。
變量的作用域規(guī)則
例:一個函數(shù)略板,讀取一個局部變量和一個全局變量
在上述例子中毁枯,python在編譯函數(shù)的定義體時,它判斷b是局部變量叮称,因為在函數(shù)中給它賦值了种玛,python會嘗試從本地環(huán)境獲取b藐鹤。后面調(diào)用f2(3)時,f2的定義體會獲取并打印局部變量a的值赂韵,但是嘗試獲取局部變量b時娱节,發(fā)現(xiàn)b沒有綁定值
實現(xiàn)一個簡單的裝飾器
下例定義了一個裝飾器,它會在每次調(diào)用被裝飾的函數(shù)時計時祭示,然后把經(jīng)過的時間括堤、傳入的參數(shù)和調(diào)用的結(jié)果打印出來。
使用clock裝飾器
上述例子中實現(xiàn)的clock裝飾器有幾個缺點:不支持關(guān)鍵字參數(shù)绍移,而且遮蓋了被裝飾函數(shù)的__name__和__doc__屬性悄窃。下述例子使用functools.wraps裝飾器把相關(guān)的屬性從func復(fù)制到clocked中。
參數(shù)化裝飾器
解析源碼中的裝飾器時蹂窖,python把被裝飾的函數(shù)作為第一個參數(shù)傳給裝飾器函數(shù)轧抗。那怎么讓裝飾器接受其他參數(shù)呢?答案是:創(chuàng)建一個裝飾器工廠函數(shù)瞬测,把參數(shù)傳給它横媚,返回一個裝飾器,然后再把它應(yīng)用到要裝飾的函數(shù)上月趟。
為了便于啟用或禁用register執(zhí)行的函數(shù)注冊功能灯蝴,我們?yōu)樗峁┮粋€可選的active參數(shù),設(shè)為false時孝宗,不注冊被裝飾的函數(shù)穷躁,示例如下,從概念上看因妇,這個新的register函數(shù)不是裝飾器问潭,而是裝飾器工廠函數(shù)。調(diào)用它會返回真正的裝飾器婚被,這才是應(yīng)用到目標函數(shù)上的裝飾器狡忙。
在上述例子中:
1、registry是一個set對象址芯,這樣添加和刪除函數(shù)的速度更快灾茁。
2、register接受一個可選的關(guān)鍵字參數(shù)谷炸。
3北专、decorate這個內(nèi)部函數(shù)是真正的裝飾器,它的參數(shù)是一個函數(shù)淑廊。
4逗余、decorate是裝飾器,必須返回一個函數(shù)季惩。
5录粱、register是裝飾器工廠函數(shù)腻格,因此返回decorate。
6啥繁、register是裝飾器工廠函數(shù)菜职,因此返回decorate。
7旗闽、@register工廠函數(shù)必須作為函數(shù)調(diào)用酬核,并且傳入所需的函數(shù)。
這里的關(guān)鍵是适室,register()要返回decorate嫡意,然后把它應(yīng)用到被裝飾的函數(shù)上。
參數(shù)化clock裝飾器
在上述示例中:
1捣辆、clock是參數(shù)化裝飾器工廠函數(shù)蔬螟。
2、decorate是真正的裝飾器汽畴。
3旧巾、clocked包裝被裝飾的函數(shù)。
4忍些、這里使用**locals()是為了在fmt中引用clocked的局部變量鲁猩。