下面是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)
现已更新
第三部分应该是seq的更新,但我不明白何时使用update_wrapper
函数。
答案 0 :(得分:7)
装饰只是用于调用另一个函数的语法糖,并用结果替换当前的函数对象。你试图理解的decorator
舞蹈过度使用了这个事实。尽管它试图让生成装饰器变得更容易,但我发现实际上并没有添加任何东西,只是因为不遵循标准做法而产生混淆。
要了解正在发生的事情,您可以使用返回值替换函数调用(包括正在应用的装饰器),并通过想象保存对原始修饰函数对象的引用来跟踪d
引用:
decorator=decorator(decorator)
通过调用自身替换原始decorator
函数。我们将忽略此处的print()
调用以使替换更容易。
decorator(decorator)
调用返回lambda fn:
update_wrapper(d(fn),fn)
,其中d
绑定到原始版本
decorator
,现在我们已经
_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)
右__name__
并在lambda
函数对象上设置此类。
@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)
我只是将@decorator
函数定义上的n_ary()
装饰器替换为返回的包含的包装函数上的@functools.wraps()
装饰器。