python 裝飾器(一):裝飾器基礎(一)裝飾器形式,何時執行


簡介

裝飾器是可調用的對象,其參數是另一個函數(被裝飾的函數)。 裝飾器可能會處理被裝飾的函數,然后把它返回,或者將其替換成另一個函數或可調用對象。

形式

假如有個名為 decorate 的裝飾器:

@decorate
def target():
    print('running target()')

上述代碼的效果與下述寫法一樣:

def target():
    print('running target()')

target = decorate(target)

兩種寫法的最終結果一樣:上述兩個代碼片段執行完畢后得到的target 不一定是原來那個 target 函數,而是 decorate(target) 返回的函數。

嚴格來說,裝飾器只是語法糖。如前所示,裝飾器可以像常規的可調用對象那樣調用,其參數是另一個函數。有時,這樣做更方便,尤其是做元編程(在運行時改變程序的行為)時。
綜上,裝飾器的一大特性是,能把被裝飾的函數替換成其他函數。第二個特性是,裝飾器在加載模塊時立即執行。

執行裝飾器

裝飾器的一個關鍵特性是,它們在被裝飾的函數定義之后立即運行。這通常是在導入時(即 Python 加載模塊時),如示例 7-2 中的registration.py 模塊所示。

示例 7-2 registration.py 模塊

registry = []  ➊
def register(func):  ➋
    print('running register(%s)' % func)  ➌
    registry.append(func)  ➍
    return func  ➎
@register  ➏
def f1():
    print('running f1()')

@register
def f2():
    print('running f2()')
def f3():  ➐
    print('running f3()')
def main():  ➑
    print('running main()')
    print('registry ->', registry)
    f1()
    f2()
    f3()

if __name__=='__main__':
    main()  ➒

❶ registry 保存被 @register 裝飾的函數引用。
❷ register 的參數是一個函數。

❸ 為了演示,顯示被裝飾的函數。
❹ 把 func 存入 registry。
❺ 返回 func:必須返回函數;這里返回的函數與通過參數傳入的一樣。
❻ f1 和 f2 被 @register 裝飾。
❼ f3 沒有裝飾。
❽ main 顯示 registry,然后調用 f1()、f2() 和 f3()。
❾ 只有把 registration.py 當作腳本運行時才調用 main()。

把 registration.py 當作腳本運行得到的輸出如下:

$ python3 registration.py
running register(<function f1 at 0x100631bf8>)
running register(<function f2 at 0x100631c80>)
running main()
registry -> [<function f1 at 0x100631bf8>, <function f2 at 0x100631c80>]
running f1()
running f2()
running f3()

注意,register 在模塊中其他函數之前運行(兩次)。調用register 時,傳給它的參數是被裝飾的函數,例如 <function f1 at 0x100631bf8>。


加載模塊后,registry 中有兩個被裝飾函數的引用:f1 和 f2。這兩個函數,以及 f3,只在 main 明確調用它們時才執行。

如果導入 registration.py 模塊(不作為腳本運行),輸出如下:

>>> import registration
running register(<function f1 at 0x10063b1e0>)
running register(<function f2 at 0x10063b268>)

此時查看 registry 的值,得到的輸出如下:

>>> registration.registry
[<function f1 at 0x10063b1e0>, <function f2 at 0x10063b268>]

示例 7-2 主要想強調,函數裝飾器在導入模塊時立即執行,而被裝飾的函數只在明確調用時運行。這突出了 Python 程序員所說的導入時和運行時之間的區別。

考慮到裝飾器在真實代碼中的常用方式,示例 7-2 有兩個不尋常的地方。

裝飾器函數與被裝飾的函數在同一個模塊中定義。實際情況是,裝飾器通常在一個模塊中定義,然后應用到其他模塊中的函數上。

register 裝飾器返回的函數與通過參數傳入的相同。實際上,大多數裝飾器會在內部定義一個函數,然后將其返回。

雖然示例 7-2 中的 register 裝飾器原封不動地返回被裝飾的函數,但是這種技術並非沒有用處。很多 Python Web 框架使用這樣的裝飾器把函數添加到某種中央注冊處,例如把 URL 模式映射到生成 HTTP 響應的函數上的注冊處。這種注冊裝飾器可能會也可能不會修改被裝飾的函數。




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM