1. 程式人生 > >淺析函式裝飾器和閉包(一)

淺析函式裝飾器和閉包(一)

函式裝飾器用於在原始碼中”標記”函式,以某種方式增強函式的行為。這是一項強大的功能,但是若想掌握,必須理解閉包。

  • 裝飾器基礎知識

裝飾器是可呼叫的物件,其引數是另一個函式(被裝飾的函式)。裝飾器可能會處理被裝飾的函式,然後把它返回,或者將其替換成另一個函式或可呼叫物件。

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

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

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

def target():
    print("running target()")

target = decorate(target)

兩種寫法的最終結果一樣:上述兩個程式碼片段執行完畢後得到的target不一定是原來那個target函式,而是decorate(target)返回的函式。
這裡寫圖片描述
嚴格來說,裝飾器只是語法糖。裝飾器可以像常規的可呼叫物件那樣呼叫,其引數是另一個函式。

綜上,裝飾器的一大特性是,能把被裝飾的函式替換成其他函式。第二個特性是,裝飾器在載入模組時立刻執行。

  • Python何時執行裝飾器

裝飾器的一個關鍵特性是,它們在被裝飾的函式定義之後立即執行。這通常是在匯入時(即Python載入模組時)

例如registration.py模組所示

# registry儲存被@register裝飾的函式引用
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()
$ python registration.py
running register(<function f1 at 0x7f27b31fb620>)
running register(<function f2 at 0x7f27b31fbbf8>)
running main()
registry -> [<function f1 at 0x7f27b31fb620>, <function f2 at 0x7f27b31fbbf8>]
running f1()
running f2()
running f3()

注意,register在模組中其他函式之前執行(兩次)。載入模組後,registry中有兩個被裝飾函式的引用: f1和f2。這兩個函式,以及f3,只有在main明確呼叫它們時才執行

如果匯入registration.py模組(不作為指令碼執行),輸出如下:

>>> import registration
running register(<function f1 at 0x7f89fb45df28>)
running register(<function f2 at 0x7f89fb46b048>)

此時檢視registry的值,得到的輸出如下:

>>> registration.registry
[<function f1 at 0x7f89fb45df28>, <function f2 at 0x7f89fb46b048>]

綜上,函式裝飾器在匯入模組時立即執行,而被裝飾的函式只有在明確呼叫時執行。另外,裝飾器通常在一個模組中定義,然後應用到其他模組中的函式上。還有就是,實際上,大多數裝飾器會在內部定義一個函式,然後將其返回。