Python类变量与实例变量重名时的异常行为解析
class TestClass(): class_v = 0 def __init__(self) -> None: self.class_v += 1 TestClass.class_v += 1 print(TestClass.class_v) # 0 class_a = TestClass() class_b = TestClass() print(f'class_a.class_v = {class_a.class_v}') # 1 print(f'class_b.class_v = {class_b.class_v}') # 2 print(TestClass.class_v) # 2
问题核心
当类变量与实例变量名称相同时,同一类创建的两个实例访问该变量结果不一致:按实例变量预期都为1,按类变量预期都为2,这背后的Python运行机制是什么?另外,若调换__init__中两行代码的执行顺序:
# 调换后的代码 TestClass.class_v += 1 self.class_v += 1
输出会变成class_a.class_v=2、class_b.class_v=3,这一现象也需要解释。
机制解释
Python中属性查找和属性赋值是完全独立的逻辑,这是问题的核心原因:
1. 原代码的执行过程
- 类定义阶段,
class_v = 0是类变量,存储在TestClass的类命名空间中,所有实例默认共享这个值(但赋值操作会打破共享)。 - 创建
class_a时:- 执行
self.class_v += 1:这是self.class_v = self.class_v + 1的语法糖。首先查找self.class_v,此时class_a的实例命名空间中没有class_v,所以会向上到类命名空间找,得到值0;计算0+1=1后,会在class_a的实例命名空间中新建一个名为class_v的实例变量,赋值为1(从此class_a访问class_v只会取自己实例里的,不再看类变量)。 - 接着执行
TestClass.class_v +=1:直接修改类命名空间中的class_v,值从0变为1。
- 执行
- 创建
class_b时:- 执行
self.class_v +=1:同样先找class_b的实例命名空间,没有则去类命名空间,此时类变量class_v是1;计算1+1=2后,在class_b的实例命名空间新建class_v=2。 - 接着执行
TestClass.class_v +=1:类变量从1变为2。
- 执行
- 最终结果:
class_a的class_v是自己的实例变量1,class_b的是自己的实例变量2,类变量TestClass.class_v是2。
2. 调换代码顺序后的执行过程
- 创建
class_a时:- 先执行
TestClass.class_v +=1:类变量从0变为1。 - 再执行
self.class_v +=1:查找self.class_v时,实例没有,取类变量的1,计算1+1=2,在class_a实例中新建class_v=2。
- 先执行
- 创建
class_b时:- 先执行
TestClass.class_v +=1:类变量从1变为2。 - 再执行
self.class_v +=1:查找时取类变量的2,计算2+1=3,在class_b实例中新建class_v=3。
- 先执行
- 最终结果:
class_a的实例变量是2,class_b的是3,类变量TestClass.class_v是2。
关键总结
- 属性查找:实例访问属性时,先找自己的实例命名空间,找不到再找类的命名空间,再找父类的命名空间。
- 属性赋值:对
self.xxx赋值时,无论类中有没有同名属性,都会直接在实例命名空间中创建/修改该实例属性,不会影响类属性(除非直接用类名.xxx赋值)。
内容的提问来源于stack exchange,提问作者ehbong
相关产品推荐
相关产品推荐

