直接在__setattr__中赋值易导致无限递归,因所有属性赋值(含内部状态)均触发它;正确做法是用object.__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)</p>
注意:这里用了 object.__getattribute__ 而不是 self._locked,避免再次触发 __getattribute__(如果重写了它)或 __getattr__。
python-docx Skill功能概述python-docx Skill是一项面向实际任务的技能,主要用于本Skill提供使用python-docx生成专业Word文档的标准方法和最佳实践;生成安全服务方案文档;核心要点生成技术架构设计文档;生成任何需要专业排版的Word文档;核心库 : python-docx;使用与执行辅助库 : docx.shared , docx.enum , docx.oxml.ns;标准代码模板;1. 文档初始化;2. 字体设置(必须!它将相关步骤、工具调用和结果整理方式集
怎么监控特定属性的变更并记录日志?
监控不是靠拦截所有赋值,而是精准识别目标属性。建议把要监控的属性名存成集合(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}")</unset>
__setattr__ 和 @property 哪个更适合写保护?
@property 更轻量、更明确,适合单个属性的细粒度控制;__setattr__ 是全局钩子,适合批量规则(比如“所有以 _ 开头的都不许改”或“仅允许白名单属性”)。
- 如果只锁 1–2 个字段,用
@property+ 私有存储(self._name)更清晰 - 如果要统一策略(如冻结整个实例),
__setattr__是唯一选择 -
@property对setattr(obj, "name", x)依然有效,但对obj.__dict__["name"] = x无效;__setattr__能拦住前者,拦不住后者 —— 这是 Python 的底层限制
真正难搞的是用户绕过接口直接操作 __dict__,这种场景下写保护本质是“君子协议”,__setattr__ 只能防误触,不能防故意破坏。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










