实例访问类变量时的“遮蔽”行为是遮蔽而非覆盖:obj.attr = value 会在实例字典中新建或修改属性,不改变类变量;可变类变量通过实例修改会真实共享;安全操作须显式用类名。

实例访问类变量时的“遮蔽”行为
不是覆盖,是遮蔽。当你写 obj.attr = value 且 attr 原本是类变量时,Python 不会修改类变量,而是在 obj.__dict__ 中新建一个同名的实例属性。后续通过 obj.attr 访问时,因“先查实例、再查类”的规则,直接命中实例属性,类变量被遮蔽——看起来像“被覆盖”,实则是查找链提前终止。
obj.attr = value 的实际执行步骤
这个赋值操作不区分左右值来源,只做一件事:向当前实例的命名空间写入键值对。它完全不检查右侧的 value 是否来自类变量,也不触发类变量更新逻辑。
- 如果
obj尚未定义attr,则新增obj.__dict__['attr'] = value - 如果
obj已有attr(无论之前是实例变量还是刚被遮蔽的类变量),则直接覆盖其值 - 类的
__dict__完全不受影响,MyClass.attr仍保持原值
可变类变量(如列表、字典)的“真共享”陷阱
当类变量是可变对象(如 list 或 dict),通过实例“读取后修改”会真正影响所有实例,因为没触发赋值操作,只是调用了原对象的方法:
class Box:
items = [] # 类变量,可变
<p>b1 = Box()
b2 = Box()
b1.items.append("book") # 修改的是同一个 list 对象
print(b2.items) # ['book'] —— 真实共享,不是遮蔽</p>
这种行为和 b1.items = ["pen"](遮蔽)有本质区别:前者是 list.append(),后者是 = 赋值。
如何安全修改类变量
必须显式通过类名操作,避免任何以实例为左值的赋值:
- ✅ 正确:
MyClass.count += 1、MyClass.config.update({...}) - ❌ 错误:
obj.count += 1(等价于obj.count = obj.count + 1,触发遮蔽) - ⚠️ 危险:
obj.items.append(x)(看似安全,实则修改共享对象)
最易忽略的是“读-改-写”模式:你以为在操作类变量,实际第一步读取后,第二步的修改可能已污染全局状态。判断依据只有一个:是否出现了 = 在实例名左侧。只要出现,就是实例级操作,与类变量无关。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











