裝飾器基礎(chǔ)知識(shí)
裝飾器是可調(diào)用的對(duì)象,其參數(shù)是另一個(gè)函數(shù)(被裝飾的函數(shù))。裝飾器可能會(huì)處理被裝飾的函數(shù),然后把它返回,或者將其替換成另一個(gè)函數(shù)或可調(diào)用對(duì)象。
假如有個(gè)名為decorate的裝飾器:
@decorate
def target():
? ? print('running target()')
上述代碼的效果與下述寫法一樣:
def target():
? ? print('running target()')
target=decorate(target)
這兩種寫法的最終結(jié)果一樣:上述兩個(gè)代碼片段執(zhí)行完畢后得到的target不一定是原來那個(gè)target函數(shù),而是decorate(target)返回的函數(shù)。
python何時(shí)執(zhí)行裝飾器
裝飾器的一個(gè)關(guān)鍵特性是,它們?cè)诒谎b飾的函數(shù)定義之后立即運(yùn)行,這通常是在導(dǎo)入(即python加載模塊時(shí))

上述腳本運(yùn)行得到的輸出如下:

上述示例主要想強(qiáng)調(diào),函數(shù)裝飾器在導(dǎo)入模塊時(shí)立即執(zhí)行,而被裝飾的函數(shù)只在明確調(diào)用時(shí)運(yùn)行,考慮到裝飾器在真實(shí)代碼中的常用調(diào)用方式,上述示例有兩個(gè)不尋常的地方:
1、裝飾器函數(shù)與被裝飾的函數(shù)在同一個(gè)模塊中定義。實(shí)際情況是,裝飾器常在一個(gè)模塊中定義,然后應(yīng)用到其他模塊的函數(shù)中。
2、registry裝飾器返回的函數(shù)與通過參數(shù)傳入的相同。實(shí)際上,大多數(shù)裝飾器會(huì)在內(nèi)部定義一個(gè)函數(shù),然后將其返回。
變量的作用域規(guī)則
例:一個(gè)函數(shù),讀取一個(gè)局部變量和一個(gè)全局變量

在上述例子中,python在編譯函數(shù)的定義體時(shí),它判斷b是局部變量,因?yàn)樵诤瘮?shù)中給它賦值了,python會(huì)嘗試從本地環(huán)境獲取b。后面調(diào)用f2(3)時(shí),f2的定義體會(huì)獲取并打印局部變量a的值,但是嘗試獲取局部變量b時(shí),發(fā)現(xiàn)b沒有綁定值
實(shí)現(xiàn)一個(gè)簡(jiǎn)單的裝飾器
下例定義了一個(gè)裝飾器,它會(huì)在每次調(diào)用被裝飾的函數(shù)時(shí)計(jì)時(shí),然后把經(jīng)過的時(shí)間、傳入的參數(shù)和調(diào)用的結(jié)果打印出來。

使用clock裝飾器

上述例子中實(shí)現(xiàn)的clock裝飾器有幾個(gè)缺點(diǎn):不支持關(guān)鍵字參數(shù),而且遮蓋了被裝飾函數(shù)的__name__和__doc__屬性。下述例子使用functools.wraps裝飾器把相關(guān)的屬性從func復(fù)制到clocked中。
參數(shù)化裝飾器
解析源碼中的裝飾器時(shí),python把被裝飾的函數(shù)作為第一個(gè)參數(shù)傳給裝飾器函數(shù)。那怎么讓裝飾器接受其他參數(shù)呢?答案是:創(chuàng)建一個(gè)裝飾器工廠函數(shù),把參數(shù)傳給它,返回一個(gè)裝飾器,然后再把它應(yīng)用到要裝飾的函數(shù)上。
為了便于啟用或禁用register執(zhí)行的函數(shù)注冊(cè)功能,我們?yōu)樗峁┮粋€(gè)可選的active參數(shù),設(shè)為false時(shí),不注冊(cè)被裝飾的函數(shù),示例如下,從概念上看,這個(gè)新的register函數(shù)不是裝飾器,而是裝飾器工廠函數(shù)。調(diào)用它會(huì)返回真正的裝飾器,這才是應(yīng)用到目標(biāo)函數(shù)上的裝飾器。

在上述例子中:
1、registry是一個(gè)set對(duì)象,這樣添加和刪除函數(shù)的速度更快。
2、register接受一個(gè)可選的關(guān)鍵字參數(shù)。
3、decorate這個(gè)內(nèi)部函數(shù)是真正的裝飾器,它的參數(shù)是一個(gè)函數(shù)。
4、decorate是裝飾器,必須返回一個(gè)函數(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的局部變量。