如何通过装饰器修改__setattr__方法的行为?
问题分析与解决
你的代码里装饰器没生效的核心原因是:Python对特殊双下划线方法(比如__setattr__)的查找逻辑是在类级别,而不是实例级别。当你执行x.b = 2时,Python实际调用的是type(x).__setattr__(x, 'b', 2),也就是类A的__setattr__,完全不会用到你绑定在实例x上的那个装饰后的方法。这就是为什么print没触发,也找不到custom_b的原因。
下面给两种符合你需求的解决方案:
方案1:装饰类的__setattr__(所有实例生效)
直接修改类的方法,这样该类的所有实例都会使用装饰后的__setattr__:
from functools import wraps class A(): pass def setattr_decorator(func): @wraps(func) def wrapper(self, name: str, value): print('called') # 调用object的__setattr__避免递归 object.__setattr__(self, 'custom_' + name, value) return wrapper # 给类A的__setattr__应用装饰器 A.__setattr__ = setattr_decorator(A.__setattr__) x = A() x.a = 2 print(x.custom_a) # 输出2 x.b = 2 print(x.custom_b) # 输出2
方案2:仅给单个实例生效
如果不想影响其他A类实例,可以给目标实例动态切换到一个临时子类,在子类中应用装饰器:
from functools import wraps class A(): pass x = A() x.a = 2 def setattr_decorator(func): @wraps(func) def wrapper(self, name: str, value): print('called') object.__setattr__(self, 'custom_' + name, value) return wrapper # 创建仅用于x的临时子类 class TempA(A): __setattr__ = setattr_decorator(A.__setattr__) # 把x的类替换为TempA x.__class__ = TempA x.b = 2 print(x.custom_b) # 输出2 # 其他A类实例不受影响 y = A() y.c = 3 print(y.c) # 输出3,不会添加custom_前缀
补充说明
- 不要直接在装饰器里调用
self.__setattr__,这会触发递归调用,必须用object.__setattr__来直接设置属性。 - 特殊方法的类级查找是Python的设计规则,目的是保证特殊方法的一致性和性能。
内容的提问来源于stack exchange,提问作者arseniybelkov
相关产品推荐
相关产品推荐

