类属性必须定义在class语句块顶层才真正生效;在__init__中用self赋值创建的是实例属性,非类属性;可变类属性被所有实例共享,易引发隐蔽bug;继承中子类定义同名类属性即独立于父类。

类属性赋值写在哪儿才真正生效
类属性必须定义在 class 语句块顶层,且不能在 __init__ 或其他方法里用 self.xxx = 赋值——那只会创建实例属性。常见错误是把本该共享的配置(比如默认超时、API 版本)写在 __init__ 里,结果每个实例都有一份副本,改一个不影响另一个。
- ✅ 正确位置:
class MyClass:下直接缩进写的count = 0、DEFAULT_TIMEOUT = 30 - ❌ 错误写法:
def __init__(self): self.DEFAULT_TIMEOUT = 30→ 这是实例属性,和类无关 - ⚠️ 动态设置类属性可用
MyClass.attr = value,但要注意:如果实例已存在同名属性,访问时会优先取实例的(覆盖类属性),不是真“修改类”
实例访问类属性时为什么有时拿到的是旧值
Python 查找属性时走 MRO 链:先查实例字典 → 再查类字典 → 再查父类。一旦实例上有了同名属性(哪怕只是读过一次并被缓存),后续再通过实例访问同名类属性,就拿不到更新后的类属性值了。
- 典型现象:
obj.x初始返回类属性值;之后执行obj.x = 99;再改MyClass.x = 100;此时obj.x仍是99,不是100 - 判断是否被遮蔽:用
hasattr(obj, 'x')是 True,但'x' in obj.__dict__也是 True,说明实例已有该属性 - 想强制读类属性:用
MyClass.x或type(obj).x,别依赖obj.x
可变类属性(如 list/dict)引发的隐蔽共享问题
类属性如果是可变对象(list、dict、自定义可变类实例),所有实例默认共享同一份内存,一个实例修改它,其他实例立刻可见——这常被误认为“bug”,其实是设计如此。
1、数据调用该功能使界面与程序分离实施变得更加容易,美工无需任何编程基础即可完成数据调用操作。2、交互设计该功能可以方便的为栏目提供个性化性息功能及交互功能,为产品栏目添加产品颜色尺寸等属性或简单的留言和订单功能无需另外开发模块。3、静态生成触发式静态生成。4、友好URL设置网页路径变得更加友好5、多语言设计1)UTF8国际编码; 2)理论上可以承担一个任意多语言的网站版本。6、缓存机制减轻服务器
- 错误示范:
class Cache: data = {}→ 所有实例共用一个data字典 - 正确做法:需要隔离时,在
__init__中初始化实例属性:self.data = {} - 真要共享可变类属性?务必加文档说明,并考虑线程安全(如用
threading.Lock包裹操作) - 检查是否误用:
id(MyClass.data)和id(obj1.data)、id(obj2.data)是否相等
继承中类属性的查找与覆盖行为
子类未定义同名类属性时,访问会沿继承链向上找;但只要子类定义了(哪怕只写 attr = ...),就完全独立于父类,改父类不影响子类,反之亦然。
立即学习“Python免费学习笔记(深入)”;
- 子类覆盖后,
Parent.attr和Child.attr指向不同对象,互不干扰 - 动态新增类属性:
Child.new_attr = 42不会影响Parent,也不会影响已存在的Child实例的__dict__ - 想让子类“继承并扩展”父类可变类属性(如共用一个注册表)?得显式引用:
Child.registry = Parent.registry,而不是靠继承自动获得
类属性和实例属性的边界看起来清晰,但实际踩坑多发生在“以为改了类属性,其实动的是实例”或“以为每个实例都有独立副本,其实共享着同一个 list”。关键就看那个属性名第一次出现在哪儿、以什么方式绑定——别信直觉,用 obj.__dict__ 和 type(obj).__dict__ 看一眼最稳。







