2013-03-25 75 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