理解帶參數的裝飾器
參考文章 Python精進-裝飾器與函數對象
加了裝飾器這段代碼從一個函數定義(不會執行任何東西,在內存單元中保存一段代碼)變成了一條賦值語句(修改被裝飾函數所在內存單元中的內容)exit
- 從運維角度看是裝飾器補充了函數的功能, 從開發角度看是裝飾器將函數作為參數
- 函數返回的函數無法被直接調用,除非將其返回值賦值個一個變量;列表就相當于這個函數,他的元素必須通過帶參數執行這個函數獲得。
- 該用函數還是裝飾器,可以看是誰調用誰,從參數傳遞的方向也可以判斷。
第一步: 定義裝飾函數
def foo(a):
def decorator(func):
def wrap(*args):
if not callable(a):
print(a)
return func(*args)
return wrap
if callable(a):
return decorator(a)
return decorator
# 裝飾器帶參數可以使得被裝飾函數的改變有兩種可能
第二步:使用裝飾器裝飾函數,調用新函數
第一種可能用法
@foo('hello')
def bar(c):
print(c)
return 0
裝飾器的處理過程
裝飾的作用是獲得一個新函數
- bar = foo('hello')(bar)
- 先執行 foo('hello') 得到 decorator 函數
- 然后讓decorator 函數帶著(bar) 一起飛
- 就變成了 bar = decorator(bar)
- decorator(bar) 執行得到一個函數 wrap(名字不重要,為了得到內存中的地址而已)
- 這個新函數可以有多個參數(因為原函數帶參數,裝飾后也要帶參數啊)
- 額外多說一句,函數里定義的函數是可變的(內存塊里存的代碼是可變的)
- wrap 怎么寫呢?根據外面的decorator函數的參數變著來:
重點來了,開始改寫bar: - 因為a='hello' not callable 所以有一行 print('hello') 的代碼
- 代碼很少,接下來就 return了,return 的是被裝飾函數的執行結果。
- 為理解為wrap函數定義為這個時候去被裝飾函數定義的內存塊執行,并將被裝飾函數的返回值返回出去。不知道這樣說是否準確???
- 相當于bar函數的函數體(內存中的塊內容)變成了:
def bar(c):
print(a)
# 執行被裝飾函數體
print(c)
# 將被裝飾函數的返回值作為新函數的返回值,還是0
return 0
調用被裝飾后的函數顯然應該為:
>>> bar('wold')
hello
world
0
第二種用法
@foo
def bar(c):
print(c)
return 0
裝飾的作用是獲得一個新函數
- foo 函數是帶參數的,所以把 bar作為參數執行 foo 函數 得到:
- bar = foo(bar)
- 仔細看foo的函數,如果參數是 callable 則返回 decorator 函數并且帶上這個 foo 的參數,所以:
- foo(bar) 變成了 decorator(bar) bar = decorator(bar)
- 先執行 foo('hello') 得到 decorator
- 于是 bar = decorator(bar)
- decorator(bar) 執行得到的是一個 wrap 函數,說白了它的名字不重要,重要的是它里面的內容:
- 里面的內容就這樣來寫 因為 a 是callable的,所以就不執行
print(a)
了,僅僅執行 func(*args),也就是將新函數的參數,被裝飾函數返回它的返回值 - 裝飾器啥也沒干,直接把被裝飾器的代碼拿去充數了。
- 相當于bar函數的函數體(內存中的塊內容)變成了:
def bar(c):
# 執行被裝飾函數
print(c)
# 將被裝飾函數的返回值作為新函數的返回值,還是0
return 0
調用被裝飾后的函數應該為:
>>> bar('wold')
world
0
思考
定義一個函數其實是分配一個內存空間來裝代碼。
decorator() 函數執行的過程就是干這個事情:
創建一個 wrap 函數,最好把函數對象的引用丟出去。
這個函數干什么嗯
它先把print('hello') 這段代碼記錄下來。
再看decorator 的參數 func 是哪個,去他的代碼塊執行它,然后把返回值返回。