內(nèi)容純屬個人理解,不對之處,歡迎指正。
之前說過,裝飾器其實就是函數(shù),既然是函數(shù),那就可以有參數(shù),裝飾器也不例外,接下來我們來分析帶參數(shù)的裝飾器。
如何構(gòu)造帶參數(shù)
帶參數(shù)倒是很簡單,在裝飾的時候給裝飾函數(shù)寫上參數(shù)就行,但是具體的裝飾器函數(shù)該怎么寫,我們需要思考一下。
我們想讓它帶參數(shù),無非就是對被裝飾函數(shù)的執(zhí)行進(jìn)行一定條件的限定設(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。