前面 decorator Part I & Part II 已經涵蓋了 decorator 困難部分8成
這裡我們再進階看一個 "參數問題"
Decorator 的下一個問題是, 如果我 decorator 要傳參數的話要怎麼辦呢?
個人覺得這個架構是 python 一個很醜很醜的敗筆
請看一下底下例子
無參數版
from functools import wraps
def first_fun(p1):
print("in first_fun")
print("p1=", p1)
def second_fun(p2, *args, **kwargs):
print("in second_fun")
print("p2=", p2)
print("args=", args)
print("kwargs=", kwargs)
p2("hello decoration!!", *args, **kwargs)
return second_fun
#無參數, function name only
@first_fun
def decoration(decor_para):
print("in decoration")
print(decor_para)
return
輸出結果
in first_fun
p1= <function decoration at 0x7f557697d620>
查一下 decoration 是什麼型態
>>> type(decoration)
<class 'function'>
<class 'function'>
* 從output 的 p1 得知, 當 decorator 沒有參數時(只是function/class 名稱時), 預設會有1個傳入值, 而此傳入值即 function decoration
* p1 是 decoration, 故 second_fun 即 nothing, 跟 decorator 無關, 故沒有被執行到
有參數版
這裡我們保留大部分 code, 只有改成 decorator 需要傳參數以方便比對
from functools import wraps
def first_fun(p1):
print("in first_fun")
print("p1=", p1)
def second_fun(p2, *args, **kwargs):
print("in second_fun")
print("p2=", p2)
print("args=", args)
print("kwargs=", kwargs)
p2("hello decoration!!", *args, **kwargs)
return second_fun
# 有參數, 第一個參數是字串"first_func parameter"
@first_fun("first_func parameter")
def decoration(decor_para):
print("in decoration")
print(decor_para)
return
輸出結果
in first_fun
p1= first_func parameter
in second_fun
p2= <function decoration at 0x7fce990862f0>
args= ()
kwargs= {}
in decoration
hello decoration!!
查一下 decoration 是什麼型態
>>> type(decoration)
<class 'NoneType'>
驚!什麼? decoration 是 None!<class 'NoneType'>
* 這裡我們發現, decorator 傳入的第1個參數 p1 不像第一個例子(decoration), p1 的內容物變成是 decorator 中你傳入的參數值了
* 然後 second_fun 的第1個參數 p2 卻變成 decoration
小結
所以這裡你就知道為什麼我們在無參數版就要放 second_fun 這個根本執行不到的 code 了吧, 程式碼都一樣, 只有有參數跟無參數的區別.
由以上結論得知, 你若要 decorator 可傳送參數, 那麼你的 decoration 只能在第二層function(second_fun) 獲得, 故你的樹(decorator)要寫兩層 function 以便獲得 decoration. 在建構你的樹的時候, 何時可以拿到 decoration 是很重要的一件事.
所以當你看到底下這堆程式碼時, 千萬不要被它嚇到了
它只不過是又要傳參數, 又要改 function name, 又要作 decorator 而已, 落落長的程式碼根本是嚇唬人而已. 其實做的功能並不多.
def render_to(tpl):
def decorator(func):
@wraps(func)
def wrapper(request, *args, **kwargs):
out = func(request, *args, **kwargs)
if isinstance(out, dict):
out = render_to_response(tpl, out, RequestContext(request))
return out
return wrapper
return decorator
進階問題
那麼, first_fun 有二個參數的話呢?
@first_fun("first_func parameter 1", "first_func parameter 2")
試試看吧, 反正就是 try and see
本文中所有的"Star("red")" 是否應該改成 decoration("red")??
回覆刪除因為您在本文中的function 名字已經變了?
Star("red") 應該要全部刪除,我當初應該只是想表達參數的傳入規則而已,當初誤植,我已經刪除,並且加入 type() 去檢視 decoration 到底獲得什麼型態
刪除