Skip to main content
 首页 » 编程设计

python之python装饰背后的机制

2025年05月04日13del

下面是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) 

好像流程应该是(我不太确定):

  1. decorator 被装饰,所以它返回 lambda fn: update_wrapper(decorator(fn),fn)。

  2. n_ary=decorator(n_ary),那么n_ary现在由于update_wrapper(decorator(n_ary),n_ary) <的函数更新了< code>

  3. 第三部分应该是seq的update,但是不明白什么时候用到update_wrapper函数。

请您参考如下方法:

修饰只是调用另一个函数并用结果替换当前函数对象的语法糖。您试图理解的 decorator 舞蹈过度使用了这一事实。尽管它试图使生成装饰器变得更容易,但我发现它实际上没有添加任何东西,只是因为不遵循标准实践而造成困惑。

要了解发生了什么,您可以将函数调用(包括应用的装饰器)替换为它们的返回值,并通过想象保存对原始装饰函数对象的引用来跟踪 d 引用:

  1. 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 被调用时被调用。

  2. @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__ 等。

  3. @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() 装饰器返回的包装函数。