2013-03-25 63 views
5

爲什麼裝飾器的參數不工作?在python中傳遞參數給裝飾器

def decAny(f0): 
    def wrapper(s0): 
     return "<%s> %s </%s>" % (any, f0(), any) 
    return wrapper 

@decAny('xxx') 
def test2(): 
    return 'test1XML' 

print(test2()) 

總是給我一個錯誤說「str是不可呼叫」 它試圖執行返回字符串包裝(內) ,而不是對其進行處理並返回結果字符串

+1

想想這樣說:你之前甚至_get_裝飾'test2',你叫'decAny('xxx')'。但'decAny'接受一個函數,'f0',而不是一個字符串。很明顯,在某個時候,'f0()'會試圖調用''xxx''。 – abarnert 2013-03-25 21:16:42

+0

好吧,但是像沒有參數的修飾器一樣,爲什麼編譯器不會假定第一個參數是客戶端函數... – ZEE 2013-03-25 21:48:15

+1

這不是參數問題。如果你有'@ decAny',那就是使用'decAny'本身作爲裝飾器。但是如果你有'@decAny()',那麼在你裝飾之前就調用'decAny',就像'@decAny('xxx')'一樣。 (這就像當你將函數作爲值傳遞時一樣,將它們存儲在變量等中,而不是調用它們) – abarnert 2013-03-25 21:57:56

回答

13

裝飾器功能返回函數。當「傳遞參數給裝飾器」時,你實際上正在調用一個返回裝飾器的函數。因此decAny()應該是一個返回函數的函數,該函數返回一個函數。

這將是這個樣子:

import functools 

def decAny(tag): 
    def dec(f0): 
     @functools.wraps(f0) 
     def wrapper(*args, **kwargs): 
      return "<%s> %s </%s>" % (tag, f0(*args, **kwargs), tag) 
     return wrapper 
    return dec 

@decAny('xxx') 
def test2(): 
    return 'test1XML' 

例子:

>>> print(test2()) 
<xxx> test1XML </xxx> 

注意,除了固定你打的具體問題,我也加入*args有點改善你的代碼, **kwargs作爲包裝函數的參數,並將它們傳遞給裝飾器中的f0調用。這使得它可以裝飾一個接受任意數量的位置或命名參數的函數,它仍然可以正常工作。

您可以閱讀上漲約functools.wraps()這裏:
http://docs.python.org/2/library/functools.html#functools.wraps

+0

[PEP 318](http://www.python.org/dev/peps/pep-0318/)有示例顯示此模式(強制實施類型屬性或接口,「同步」等)。 – abarnert 2013-03-25 21:15:41

+1

如果您要添加'* args,** kwargs'來改進他的代碼(不解釋原因),您可能還想添加'functools.wraps'。 – abarnert 2013-03-25 21:17:23

+0

@abarnert感謝您的建議,增加了'functools.wraps'和一些額外的解釋。 – 2013-03-25 21:22:26

1

有一個從「馬克·魯茨 - Python入門」的良好樣本:

def timer(label=''): 
    def decorator(func): 
     def onCall(*args): # Multilevel state retention: 
      ...    # args passed to function 
      func(*args)  # func retained in enclosing scope 
      print(label, ... # label retained in enclosing scope 
     return onCall 
    return decorator   # Returns the actual decorator 

@timer('==>')    # Like listcomp = timer('==>')(listcomp) 
def listcomp(N): ...   # listcomp is rebound to new onCall 

listcomp(...)    # Really calls onCall