类变量被实例用=赋值时新建实例属性,而调用可变对象原地方法(如append)会真正修改类变量;类变量在class下定义、所有实例共享,实例变量通过self.xxx=创建、各实例独有。

类变量和实例变量改写时行为完全不同
直接说结论:类变量被实例修改时,如果用 = 赋值,实际是给该实例新建一个同名实例属性;而调用可变对象的原地方法(如 append())会真正影响类变量本身。这是最常踩的坑。
常见错误现象:MyClass.items.append(1) 后所有实例都看到新增元素,但 self.items = [1] 后只有当前实例有变化。
- 类变量定义在 class 下、方法外,所有实例共享同一份内存地址
- 实例变量通过
self.xxx = ...创建,每个实例独有一份 - 不可变类型(
int、str、tuple)赋值必然触发新绑定;可变类型(list、dict)原地操作才真正共享
初始化类变量要避开可变默认参数陷阱
别在 __init__ 参数里写 items=[]——这会让所有实例悄悄共享同一个列表。类变量初始化必须在 class 体里显式声明。
使用场景:你想让所有实例默认带一个空列表,但又不希望它们互相污染。
立即学习“Python免费学习笔记(深入)”;
- ✅ 正确:
class Counter:<br> items = [] # 类变量,明确定义
- ❌ 错误:
def __init__(self, items=[]): # 默认参数只创建一次<br> self.items = items
- ⚠️ 注意:
Counter.items is Counter().items是False,因为后者绑定了实例变量;但Counter.items is AnotherCounter().items是True(如果没被覆盖)
访问类变量优先级:实例 > 类,但修改逻辑相反
读取时,Python 先查实例有没有这个属性,没有才去类里找;但写入时,self.xxx = ... 永远只作用于实例,不会修改类变量本身。
性能影响:频繁读取类变量比读实例变量略慢(多一次字典查找),但差异微乎其微;重点在语义清晰,别靠读写行为反推逻辑。
- 读:
obj.name→ 先查obj.__dict__.get('name'),再查type(obj).__dict__.get('name') - 写:
obj.name = 'x'→ 只写进obj.__dict__,不影响类 - 想改类变量?必须用
MyClass.name = 'x'或type(obj).name = 'x'
判断某个属性到底属于实例还是类
别猜,用 hasattr() 和 vars() 直接看内存归属。类变量不在实例 __dict__ 里,但能被实例读到。
容易忽略的点:继承链中,类变量可能来自父类,vars(MyClass) 不包含父类定义的类变量,得用 MyClass.__dict__ 或 inspect.getmembers()。
- 检查实例是否有:
'items' in vars(obj) - 检查类是否有(不含继承):
'items' in vars(MyClass) - 查真实来源:
getattr(obj, 'items') is MyClass.items—— 如果为True,说明没被实例覆盖过
类变量和实例变量的边界,不在语法上,而在你是否执行了「赋值语句」以及「赋值目标是不是实例命名空间」。很多人卡在“为什么我改了却没生效”,其实只是忘了自己写的那行 self.xxx = 已经悄悄切断了和类变量的连接。










