下面是python中装饰器的一个例子。我不太明白双重装饰器的实际工作原理。
from functools import update_wrapper
def decorator(d):
print(d.__name__)
return lambda fn: update_wrapper(d(fn),fn)
decorator=decorator(decorator) #I don't understand how this works.
@decorator
def n_ary(f):
print(f.__name__)
def n_ary_f(x,*args):
return x if not args else f(x,n_ary_f(*args))
return n_ary_f
@n_ary
def seq(x,y):return ('seq',x,y)
好像流程应该是(我不太确定):
decorator被装饰,所以它返回lambda fn: update_wrapper(decorator(fn),fn)。n_ary=decorator(n_ary),那么n_ary现在由于update_wrapper(decorator(n_ary),n_ary) <的函数更新了< code>的函数更新了<>第三部分应该是seq的update,但是不明白什么时候用到
update_wrapper函数。
请您参考如下方法:
修饰只是调用另一个函数并用结果替换当前函数对象的语法糖。您试图理解的 decorator 舞蹈过度使用了这一事实。尽管它试图使生成装饰器变得更容易,但我发现它实际上没有添加任何东西,只是因为不遵循标准实践而造成困惑。
要了解发生了什么,您可以将函数调用(包括应用的装饰器)替换为它们的返回值,并通过想象保存对原始装饰函数对象的引用来跟踪 d 引用:
decorator=decorator(decorator) 将原来的 decorator 函数替换为对其自身的调用。我们将忽略此处的 print() 调用以使替换更容易。
decorator(decorator) 调用返回 lambda fn: update_wrapper(d(fn),fn),其中d绑定(bind)到原来的 装饰器,所以现在我们有
_saved_reference_to_decorator = decorator
decorator = lambda fn: update_wrapper(_saved_reference_to_decorator(fn), fn)
所以 update_wrapper() 还没有真正被调用。它只会在这个新的 decorator lambda 被调用时被调用。
@decorator 然后调用上面的 lambda(调用 _saved_reference_to_decorator(fr) 并将结果传递给 update_wrapper()) 并将该 lambda 应用于 def n_ary(f) 函数:
n_ary = decorator(n_ary)
扩展为:
n_ary = update_wrapper(_saved_reference_to_decorator(n_ary), n_ary)
即:
_saved_reference_to_n_ary = n_ary
n_ary = update_wrapper(lambda fn: update_wrapper(_saved_reference_to_n_ary(fn), fn), n_ary)
现在,update_wrapper()只是将元数据从第二个参数复制到第一个返回第一个参数的元数据,这样就离开了:
n_ary = lambda fn: update_wrapper(_saved_reference_to_n_ary(fn), fn)
在 lambda 函数对象上设置正确的 __name__ 等。
@n_ary 再次被应用装饰器,这次应用到 def seq(x, y),所以我们得到:
seq = n_ary(seq)
可以扩展为:
seq = update_wrapper(_saved_reference_to_n_ary(seq), seq)
如果我们取 update_wrapper() 的返回值是
seq = _saved_reference_to_n_ary(seq)
将元数据从原始 seq 复制到原始 n_ary 函数返回的任何内容。
所以最后,所有这一切让你得到的是 update_wrapper() 被应用于装饰器的返回值,这是包含的包装器函数。
这一切都是,太复杂了。 update_wrapper() 函数已经提供了一个更具可读性的辅助装饰器:@functools.wraps() .您的代码可以重写为:
import functools
def n_ary(f):
print(f.__name__)
@functools.wraps(f)
def n_ary_f(x,*args):
return x if not args else f(x,n_ary_f(*args))
return n_ary_f
@n_ary
def seq(x,y):return ('seq',x,y)
我只是将 n_ary() 函数定义上的 @decorator 装饰器替换为包含的 @functools.wraps() 装饰器返回的包装函数。
