內(nèi)容純屬個人理解奈揍,不對之處曲尸,歡迎指正。
之前說過男翰,裝飾器其實就是函數(shù)另患,既然是函數(shù),那就可以有參數(shù)蛾绎,裝飾器也不例外昆箕,接下來我們來分析帶參數(shù)的裝飾器。
如何構(gòu)造帶參數(shù)
帶參數(shù)倒是很簡單租冠,在裝飾的時候給裝飾函數(shù)寫上參數(shù)就行鹏倘,但是具體的裝飾器函數(shù)該怎么寫,我們需要思考一下顽爹。
我們想讓它帶參數(shù)纤泵,無非就是對被裝飾函數(shù)的執(zhí)行進行一定條件的限定設(shè)置,也就是說在函數(shù)執(zhí)行之前镜粤,函數(shù)就必須具備這個條件捏题,那么這樣的話,就有兩種選擇肉渴,要么在傳遞func以后傳遞參數(shù)公荧;要么在傳遞func之前傳遞參數(shù)。
在想法上同规,兩種其實是都可以的循狰,但是窟社,我們傳遞參數(shù)的時候是給裝飾器傳遞,也就是說绪钥,裝飾器函數(shù)首先接收的是裝飾器函數(shù)的參數(shù)焰枢,然后帶著這個參數(shù)再去裝飾函數(shù)瓶颠。因此答倡,我們就必須先處理裝飾器函數(shù)的參數(shù)焕数,然后再去處理被裝飾的函數(shù)。
所以在實現(xiàn)上跪楞,我們應(yīng)該這樣去構(gòu)造帶參裝飾器函數(shù):
def deco_para(parameter):
def deco_func(func):
def wrapper(*args, **kwargs):
print(parameter)
func(*args, **kwargs)
return wrapper
return deco_func
帶參裝飾器示例
def deco_para(parameter):
print('enter deco_para')
def deco_func(func):
print('enter deco_func')
def wrapper(*args, **kwargs):
print('enter wrapper')
print(parameter)
print('---wrapper: before func---')
func(*args, **kwargs)
print('---wrapper: after func---')
return wrapper
return deco_func
@deco_para(123)
def foo():
print('---foo---')
if __name__ == '__main__':
print('--start--')
foo()
運行結(jié)果:
enter deco_para
enter deco_func
--start--
enter wrapper
123
---wrapper: before func---
---foo---
---wrapper: after func---
結(jié)果應(yīng)該不用多說缀去,先接收參數(shù)123,然后接收函數(shù)foo甸祭,最后執(zhí)行wrapper缕碎。
裝飾過程解析--多次輸出問題
我們解析下過程
deco_func = deco_para(123) # 接收參數(shù)123
wrapper = deco_func(foo) # 接收函數(shù)foo
foo = wrapper # 重命名
foo() # 執(zhí)行foo
如果你試著按這個過程去執(zhí)行代碼,會發(fā)現(xiàn)一個問題池户,wrapper函數(shù)里面的代碼執(zhí)行了2次咏雌。
enter deco_para
enter deco_func
enter deco_para
enter deco_func
enter wrapper
123
---wrapper: before func---
enter wrapper
123
---wrapper: before func---
---foo---
---wrapper: after func---
---wrapper: after func---
我們看下究竟是怎么回事。
# 程序執(zhí)行校焦,掃面到裝飾器赊抖,執(zhí)行裝飾器函數(shù)內(nèi)部代碼
1.enter deco_para
2.enter deco_func
# deco_para(123)接收參數(shù)123時執(zhí)行3
3.enter deco_para
# deco_func(foo)接收函數(shù)foo時執(zhí)行4
4.enter deco_func
# foo()執(zhí)行foo()
5.enter wrapper
6.123
7.---wrapper: before func---
8.enter wrapper
9.123
10.---wrapper: before func---
11.---foo---
12.---wrapper: after func---
13.---wrapper: after func---
在之前解析裝飾器的時候就提到過,函數(shù)被裝飾以后寨典,就不是原來的函數(shù)了氛雪,也就是說上面所執(zhí)行的foo,其實是wrapper耸成。
那么wrapper里面有什么東西呢报亩?
print('enter wrapper')
print(parameter)
print('---wrapper: before func---')
func(*args, **kwargs)
print('---wrapper: after func---')
似乎執(zhí)行結(jié)果應(yīng)該是
enter wrapper
123
---wrapper: before func---
---foo---
---wrapper: after func---
但是事實上,此時的wrapper里面的func已經(jīng)不是原來的func井氢。
回顧閉包:引用了自由變量的函數(shù)即是一個閉包弦追,這個被引用的自由變量和這個函數(shù)一同存在, 即使已經(jīng)離開了了創(chuàng)造它的環(huán)境也不例外。
那么可以推測花竞,此時的被裝飾函數(shù)應(yīng)該具有了額外的東西劲件,這些東西就是
print('enter wrapper')
print(parameter)
print('---wrapper: before func---')
print('---wrapper: after func---')
由此也就可以知道為什么wrapper里面的代碼執(zhí)行了2次:就是在執(zhí)行到func(*args, **kwargs)的時候,執(zhí)行了功能豐富以后的func约急。