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

如何通过装饰器修改__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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 03:55:39