同名不违法但读写行为不一致,实例赋值仅新增实例属性而非覆盖类属性,访问优先级为实例__dict__→类__dict__→父类,需用字典检查明确归属。

直接说结论:同名不违法,但读写行为不一致,容易误判数据归属——关键不是“怎么办”,而是“怎么让它按你预期工作”。
实例赋值 obj.attr = value 永远只写入实例字典
哪怕类里早有 attr = 100,执行 a.x = 20 后,a.__dict__ 里就多了一个 'x' 键,而 A.x 完全不变。这不是“覆盖”,是新增。
- 常见错误现象:
hasattr(a, 'x')为True,但hasattr(A, 'x')也为True,你以为改了类属性,其实只动了实例 - 调试建议:用
'x' in a.__dict__和'x' in A.__dict__分别确认来源,比打印值更可靠 - 性能影响:后续访问
a.x不再查类字典,快一点;但多人协作时,别人很难从a.x推断出它是实例独有还是默认值
想改类属性,必须显式通过类名或 __class__
写 a.x = 99 改不了类属性;要改,得写 A.x = 99 或 a.__class__.x = 99。后者在继承场景下更安全,避免硬编码类名。
- 注意:
self.__class__.x += 1是常见陷阱——如果self.x已存在(比如之前赋过值),+=会先读self.x再写回self.__dict__,根本碰不到类属性 - 正确计数模式:
self.__class__.count = self.__class__.count + 1(避免就地修改) - 可变类属性(如
data = [])尤其危险:所有未覆盖该属性的实例共享同一对象,self.data.append(...)看似操作实例,实则污染全局
删除 del obj.attr 只删实例层,不碰类属性
del a.x 后,a.x 不会报错,而是自动回落到 A.x 的值。这常被误当作“恢复默认”,其实是查找链自然生效。
立即学习“Python免费学习笔记(深入)”;
- 不能用
del a.x删除类属性;真要删,得写del A.x - 误删后访问未定义属性:若
a没x、A也没x,a.x抛AttributeError - 检查是否只剩类属性可用:
not hasattr(a, '__dict__') or 'x' not in a.__dict__,再配合hasattr(A, 'x')
方法、类属性、实例属性三者同名时优先级最混乱
当 name 既是实例属性,又是类方法,还可能是类属性,Python 查找顺序是:实例 __dict__ → 类 __dict__(含方法)→ 父类。一旦实例有 name,方法就被遮蔽,obj.name() 直接报 TypeError。
- 典型错误:
p = Person('Alice'); p.name()——p.name是字符串,不是方法 - 补救方式有限:只能用
Person.name(p)显式调用,或通过getattr(Person, 'name')(p) - 最佳实践:命名就区分开,比如实例用
_name或full_name,方法用get_name()或to_str()
最容易被忽略的是:实例赋值创建属性这一步没有提示、不可撤销(除非 del),且在多继承或动态属性场景下,__dict__ 查找可能跳过中间类——别依赖“看起来一样”来判断数据在哪一层。


















