正如其他答案所解释的,装饰器在调用时通常会传递一个隐式函数参数,只需使用它们的名称,如下所示:
@deco
def somefunc(...): pass
与以下内容相同:
def somefunc(...): pass
somefunc = deco(somefunc)
这种情况下的参数是紧随其后的函数定义的编译版本。在这种情况下,装饰器返回一个分配给函数名称的可调用对象,而不是通常情况下的编译函数体。
但是,如果一个装饰器函数在被调用时被显式地赋予一个或多个参数,像这样:
@deco(args)
def somefunc(...): pass
变成等价于:
def somefunc(...): pass
somefunc = deco(args)(somefunc)
如您所见,这种情况下的工作方式有所不同。装饰器函数仍然返回一个可调用的,只是这一次 that 需要一个“普通的”单个隐式参数装饰器函数,然后像以前一样使用来自以下函数定义的函数对象调用。
再次——正如其他人所指出的——这使得装饰器显式传递参数,装饰器工厂在某种意义上他们构造并返回“常规”装饰器函数。
在大多数情况下,装饰器可以实现为函数或类,因为两者都可以在 Python 中调用。就个人而言,我发现函数更容易理解,因此将在下面使用该方法。另一方面,函数方法可能会变得很棘手,因为它通常涉及一个或多个嵌套函数定义。
以下是如何为模块中的do()函数编写装饰器。在下面的代码中,我定义了它的作用是在调用函数之前打印出函数的参数。
def do(fn, args=tuple(), kwargs={}, priority=0,
block=False, timeout=0, callback=None, daemon=False):
# show arguments
print ('in do(): fn={!r}, args={}, kwargs={}, priority={},\n'
' block={}, timeout={}, callback={}, daemon={}'
.format(fn.__name__, args, kwargs, priority,
block, timeout, callback, daemon))
# and call function 'fn' with its arguments
print (' calling {}({}, {})'.format(
fn.__name__,
', '.join(map(str, args)) if args else '',
', '.join('{}={}'.format(k, v) for k,v in kwargs.items())
if kwargs else '')
)
fn(*args, **kwargs)
def do_decorator(**do_kwargs):
def decorator(fn):
def decorated(*args, **kwargs):
do(fn, args, kwargs, **do_kwargs)
return decorated
return decorator
@do_decorator(priority=2)
def decoratedTask(arg, dic=42):
print 'in decoratedTask(): arg={}, dic={}'.format(arg, dic)
decoratedTask(72, dic=3)
输出:
in do(): fn='decoratedTask', args=(72,), kwargs={'dic': 42}, priority=2,
block=False, timeout=0, callback=None, daemon=False
calling decoratedTask(72, dic=3)
in decoratedTask(): arg=72, dic=3
以下是这个看起来很复杂的东西如何工作的详细说明:
外部装饰器函数do_decorator(),定义了它返回的另一个内部装饰器函数,这里创造性地命名为decorator。
Whatdecoratordoes 定义了在简单的“无参数”场景中修饰的函数会发生什么——这里定义并返回另一个——但最终的——嵌套函数decorated,它只调用模块的do()function 并从调用点传递两个参数(如果有的话),以及那些供do()function 使用的参数。
这个用例有点复杂,因为外部装饰器和被装饰的函数都有关键字参数。需要特别注意确保每个关键字的名称都是唯一的,以免它们发生冲突(并且 mutablekwargsargument 默认值不会被do()函数中的某些内容无意更改)。