You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

组合装饰器时如何给被装饰函数加属性?为何第二个装饰器检测失败?

为何第二个装饰器检测返回False?

定义了两个函数装饰器,目标是检测函数是否应用了0个、1个或2个装饰器。以下代码运行后,第二个装饰器的检测结果为False,原因是什么?

示例代码

def decorator1(f):
    def wrapped(*args, **kwargs):
        f(*args, **kwargs)

    wrapped.dec1 = True
    return wrapped


def decorator2(f):
    def wrapped(*args, **kwargs):
        f(*args, **kwargs)

    wrapped.dec2 = True
    return wrapped


@decorator1
@decorator2
def myfunc():
    print(f"running myfunc")


if __name__ == "__main__":
    myfunc()

    print(f"myfunc has decorator1: {getattr(myfunc, 'dec1', False)}")
    print(f"myfunc has decorator2: {getattr(myfunc, 'dec2', False)}")

运行结果

running myfunc
myfunc has decorator1: True 
myfunc has decorator2: False

原因分析

Python装饰器的执行顺序是从下往上的:

  1. 首先@decorator2装饰原始的myfunc,返回一个带有dec2=True标记的wrapped函数(暂称wrapped2)。
  2. 接着@decorator1装饰的是wrapped2,但decorator1返回的是自己创建的新wrapped函数(wrapped1),这个新函数只添加了dec1=True标记,完全没有继承wrapped2上的dec2属性。
  3. 最终变量myfunc指向的是decorator1返回的wrapped1,自然找不到dec2属性,所以检测返回False。

解决方法

要让装饰器保留之前装饰器添加的属性,有两种常用方式:

方式1:使用functools.wraps

functools.wraps是Python标准库提供的工具,它会自动将原函数的元信息(包括自定义属性)复制到装饰器的wrapped函数上。

修改后的代码:

import functools

def decorator1(f):
    @functools.wraps(f)
    def wrapped(*args, **kwargs):
        return f(*args, **kwargs)
    wrapped.dec1 = True
    return wrapped


def decorator2(f):
    @functools.wraps(f)
    def wrapped(*args, **kwargs):
        return f(*args, **kwargs)
    wrapped.dec2 = True
    return wrapped


@decorator1
@decorator2
def myfunc():
    print(f"running myfunc")


if __name__ == "__main__":
    myfunc()

    print(f"myfunc has decorator1: {getattr(myfunc, 'dec1', False)}")
    print(f"myfunc has decorator2: {getattr(myfunc, 'dec2', False)}")

方式2:手动复制属性

如果不想依赖标准库,可以手动将原函数的属性字典复制到新的wrapped函数上:

修改后的代码:

def decorator1(f):
    def wrapped(*args, **kwargs):
        f(*args, **kwargs)
    # 复制原函数的所有属性
    wrapped.__dict__.update(f.__dict__)
    wrapped.dec1 = True
    return wrapped


def decorator2(f):
    def wrapped(*args, **kwargs):
        f(*args, **kwargs)
    # 复制原函数的所有属性
    wrapped.__dict__.update(f.__dict__)
    wrapped.dec2 = True
    return wrapped


@decorator1
@decorator2
def myfunc():
    print(f"running myfunc")


if __name__ == "__main__":
    myfunc()

    print(f"myfunc has decorator1: {getattr(myfunc, 'dec1', False)}")
    print(f"myfunc has decorator2: {getattr(myfunc, 'dec2', False)}")

两种方式运行后,都会输出:

running myfunc
myfunc has decorator1: True 
myfunc has decorator2: True

内容的提问来源于stack exchange,提问作者Davy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 00:53:12