如何传递额外的参数到Python装饰器?

我有一个像下面的装饰器

def myDecorator(test_func): return callSomeWrapper(test_func) def callSomeWrapper(test_func): return test_func @myDecorator def someFunc(): print 'hello' 

我想增强这个装饰器接受下面的另一个参数

 def myDecorator(test_func,logIt): if logIt: print "Calling Function: " + test_func.__name__ return callSomeWrapper(test_func) @myDecorator(False) def someFunc(): print 'Hello' 

但是这个代码给出了错误,

 TypeError: myDecorator() takes exactly 2 arguments (1 given) 

为什么函数不能自动传递? 我如何明确地将函数传递给装饰器函数?

既然你像一个函数一样调用装饰器,它需要返回另一个实际的装饰器的函数:

 def my_decorator(param): def actual_decorator(func): print("Decorating function {}, with parameter {}".format(func.__name__, param)) return function_wrapper(func) # assume we defined a wrapper somewhere return actual_decorator 

外部函数将被赋予你显式传递的任何参数,并且应该返回内部函数。 内部函数将传递函数进行装饰,并返回修改后的函数。

通常你希望装饰器通过将它包装在一个包装函数中来改变函数行为。 这是一个可选的添加日志loggingfunction的例子:

 def log_decorator(log_enabled): def actual_decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): if log_enabled: print("Calling Function: " + func.__name__) return func(*args, **kwargs) return wrapper return actual_decorator 

functools.wraps调用将名称和文档string等内容复制到包装函数中,使其与原始函数更类似。

用法示例:

 >>> @log_decorator(True) ... def f(x): ... return x+1 ... >>> f(4) Calling Function: f 5 

只是为了提供一个不同的观点:语法

 @expr def func(...): #stuff 

相当于

 def func(...): #stuff func = expr(func) 

特别是, expr可以是任何你喜欢的,只要它评估为一个可调用的。 特别是, expr可以是一个装饰器工厂:你给它一些参数,它给你一个装饰器。 所以也许更好的方法来了解你的情况是

 dec = decorator_factory(*args) @dec def func(...): 

然后可以缩短到

 @decorator_factory(*args) def func(...): 

当然,因为看起来decorator_factory是一个装饰器,所以人们倾向于命名它来反映这一点。 当你试图去追求间接的层次时,可能会引起混淆。

只是想添加一些有用的技巧,将允许使装饰参数可选。 它也会重用装饰器并减less嵌套

 import functools def myDecorator(test_func=None,logIt=None): if not test_func: return functools.partial(myDecorator, logIt=logIt) @functools.wraps(test_func) def f(*args, **kwargs): if logIt==1: print 'Logging level 1 for {}'.format(test_func.__name__) if logIt==2: print 'Logging level 2 for {}'.format(test_func.__name__) return test_func(*args, **kwargs) return f #new decorator myDecorator_2 = myDecorator(logIt=2) @myDecorator(logIt=2) def pow2(i): return i**2 @myDecorator def pow3(i): return i**3 @myDecorator_2 def pow4(i): return i**4 print pow2(2) print pow3(2) print pow4(2) 

只是另一种做装饰的方式。 我觉得这种方法最容易把我的头围绕。

 import functools class NiceDecorator(object): def __init__(self, param_foo='a', param_bar='b'): self.param_foo = param_foo self.param_bar = param_bar def __call__(self, func): @functools.wraps(func) def my_logic(*args, **kwargs): # whatever logic your decorator is supposed to implement goes in here print('pre action baz') print(self.param_bar) # including the call to the decorated function (if you want to do that) result = func(*args, **kwargs) print('post action beep') return result return my_logic # usage example from here on @NiceDecorator(param_bar='baaar') def example(): print('example yay') example()