直接在__setattr____________________________________________.__setattr__(self, name, value)绕过钩子。为什么直接在那里?
__setattr__ 内阻属性赋值容易出错?
因为 Python 的 __setattr__ 当**的所有属性都被赋值时,它将被无差别地触发**,包括 self._locked = True 这种内部状态设置。如果处理不当,将陷入无限递归或报告 RecursionError —— 比如你在 __setattr__ 里又尝试给 self._locked 赋值,会反复调用自己。
正确的做法是绕过 __setattr__ 直接操作实例字典:object.__setattr__(self, name, value)。这是安全写入内部状态的唯一途径。
- 所有初始逻辑(例如
_locked、_watched_attrs)必须用object.__setattr__设置 - 不能在
__setattr__里调用self.xxx = yyy,即使只是设一个 flag - 类属性和实例属性没有区别,
__setattr__只是实例层面的赋值
关键在于区分「初始化阶段」和「运行时赋值」。常见的做法是加一个 _initialized 标志位,在 __init__ 结尾设为 True,然后拦截写作操作。
def __init__(self):
object.__setattr__(self, "_initialized", False)
object.__setattr__(self, "name", "default")
object.__setattr__(self, "_locked", False)
object.__setattr__(self, "_initialized", True)
<p>def <strong>setattr</strong>(self, name, value):
if not object.<strong>getattribute</strong>(self, "_initialized"):
object.<strong>setattr</strong>(self, name, value)
return
if name in ["name", "age"] and object.<strong>getattribute</strong>(self, "_locked"):
raise AttributeError(f"Cannot modify read-only attribute '{name}'")
object.<strong>setattr</strong>(self, name, value)注:在这里使用 object.__getattribute__ 而不是 self._locked,避免再次触发 __getattribute__(如果重写)或 __getattr__。
立即学习“Python免费学习笔记(深入);
Python 3.14.2
Python 3.14.2是Python编程语言于2025年12月5日发布的稳定版本,属于3.14系列的第二次维护更新。该版本包含18个修复项目,重点解决多过程、数据和正则表达模块的回归问题,修复CVE-2025-12084等安全漏洞。这个版本标志着Python发展的一个重要里程碑,即自由线程模式(删除GIL)正式得到官方支持。
下载 如何监控特定属性的变化并记录日志?监控不是通过拦截所有赋值,而是通过准确识别目标属性。建议将要监控的属性名存储集成(set),在 __setattr__ 中判断 name in self._watched_attrs。
- 日志建议用
print或logging.info,别用print在生产环境中,但是调试时足够了 - 避免在监控逻辑中修改其他属性,否则可能会触发二次
__setattr__ - 如果属性值是可变对象(如
list),改其内容(obj.items.append(x))不会触发__setattr__—— 这是一个常见的盲点
示例片段:
object.__setattr__(self, "_watched_attrs", {"name", "status"})
# …
if name in object.__getattribute__(self, "_watched_attrs"):
old = getattr(self, name, "<unset>")
print(f"[WATCH] {name} changed: {old!r} → {value!r}")
__setattr__ 和 @property 哪个更适合写作保护?
@property 细粒度控制更轻、更清晰,适用于单个属性;__setattr__ 适用于批量规则(如“所有”) _ 一开始不允许改变”或“只允许白名单属性”)。
- 如果只锁 1–2 个字段,用
@property+ 私有存储(self._name)更清晰 - 若要统一策略(如冻结整个实例),
__setattr__是唯一选择 -
@property对setattr(obj, "name", x)还是有效的,但是对的obj.__dict__["name"] = x无效;__setattr__能阻止前者,不能阻止后者 —— 这是 Python 的底层限制
真正困难的是用户绕过界面直接操作 __dict__,在这种情况下,保护的本质是“君子协议”,__setattr__ 只能防止误触,不能防止故意破坏。