淺析函式裝飾器和閉包(一)
阿新 • • 發佈:2019-02-18
函式裝飾器用於在原始碼中”標記”函式,以某種方式增強函式的行為。這是一項強大的功能,但是若想掌握,必須理解閉包。
- 裝飾器基礎知識
裝飾器是可呼叫的物件,其引數是另一個函式(被裝飾的函式)。裝飾器可能會處理被裝飾的函式,然後把它返回,或者將其替換成另一個函式或可呼叫物件。
假如有個名為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>]
綜上,函式裝飾器在匯入模組時立即執行,而被裝飾的函式只有在明確呼叫時執行。另外,裝飾器通常在一個模組中定義,然後應用到其他模組中的函式上。還有就是,實際上,大多數裝飾器會在內部定義一個函式,然後將其返回。