setattr在自定义__setattr__中触发无限递归,因所有属性赋值均调用该方法;安全做法是直接操作self.__dict__[name] = value(无__slots__时)或调用object.__setattr__(self, name, value)。
setattr 在自定义 __setattr__ 里会触发无限递归当你在类的 __setattr__ 方法内部直接调用 setattr(self, name, value),Python 会再次进入同一个 __setattr__,形成死循环。这不是 bug,而是机制:所有属性赋值(包括 self.x = y 和 setattr(self, ...))都会走 __setattr__。
最常用、最安全的方式是跳过 __setattr__,把值直接写进实例的 __dict__:
def __setattr__(self, name, value):
if name == "special_flag":
# 自定义逻辑
super().__setattr__(name, value.upper()) # 或其他处理
else:
# 防护:不触发 __setattr__,避免递归
self.__dict__[name] = value
self.__dict__[name] = value 是底层字典操作,完全绕过 __setattr__
__slots__ 的类不生效(此时需用 object.__setattr__(self, name, value))object.__setattr__ 显式委托当类继承链复杂,或你希望保持与父类 __setattr__ 行为一致(比如父类做了验证),应显式调用基类实现:
def __setattr__(self, name, value):
if name == "counter":
if not isinstance(value, int):
raise TypeError("counter must be int")
# 委托给 object.__setattr__,不触发当前类重写的 __setattr__
object.__setattr__(self, name, value)
else:
super().__setattr__(name, value) # 或继续委托给父类
object.__setattr__(self, name, value) 是 Python 属性赋值的最终实现,不经过任何自定义 __setattr__
self.__dict__ 更通用:兼容 __slots__、描述符、property setter(只要它们没被覆盖)super().__setattr__(...) —— 如果父类也重写了 __setattr__,仍可能递归以下写法看似“手动”,实则仍在触发 __setattr__:
self.name = value → 触发当前 __setattr__
setattr(self, name, value) → 触发当前 __setattr__
self.__dict__.update({name: value}) → 看似字典操作,但某些情况下(如 __dict__ 被代理或封装)可能间接触发__init__ 中未做防护就调用 setattr → 同样递归,因为 __init__ 里 self.x = y 也会走 __setattr__
真正安全的只有两种:直接操作 self.__dict__(无 __slots__ 时),或明确调用 object.__setattr__。其余都是幻觉。