组合装饰器时如何给被装饰函数加属性?为何第二个装饰器检测失败?
为何第二个装饰器检测返回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装饰器的执行顺序是从下往上的:
- 首先
@decorator2装饰原始的myfunc,返回一个带有dec2=True标记的wrapped函数(暂称wrapped2)。 - 接着
@decorator1装饰的是wrapped2,但decorator1返回的是自己创建的新wrapped函数(wrapped1),这个新函数只添加了dec1=True标记,完全没有继承wrapped2上的dec2属性。 - 最终变量
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
相关产品推荐
相关产品推荐

