类变量和实例变量的作用域分别是什么?
类变量 & 实例变量 作用域完整区分
一、先理清两个核心概念
- 作用域 = 能访问到该变量的代码范围
- 两者载体不同:
- 类变量挂载在类上
- 实例变量挂载在单个实例 self上
二、实例变量(self.xxx)作用域
1. 可访问范围
- 当前实例内部所有方法(实例方法、类方法不行、静态方法无 self)
python
运行
class A: def __init__(self): self.name = "张三" # 实例变量 def test(self): print(self.name) # ✅ 实例方法内可访问 - 外部代码:通过实例对象访问
python
运行
a = A() print(a.name) # ✅ 外部通过实例读取
2. 不可访问范围
- 类方法
@classmethod中,只用cls拿不到实例变量; - 静态方法
@staticmethod无 self、无 cls,完全无法访问; - 直接用类名
A.name访问,报错; - 其他实例不能访问另一个实例的实例变量:
python
运行
a1 = A() a2 = A() print(a1.name) print(a2.name) # a2 不能读取 a1 的 self.name,各自独立
3. 生命周期绑定作用域
实例销毁后,实例变量作用域直接消失,全局、其他类都拿不到。
三、类变量(类内直接定义,self 外)作用域
1. 可访问范围
- 类内部任意位置
- 实例方法:
self.类变量名/类名.类变量名 - 类方法:
cls.类变量名 - 静态方法:
类名.类变量名
- 实例方法:
- 外部代码两种访问方式
python
运行
class A: count = 0 # 方式1(推荐) print(A.count) # 方式2(仅读取,赋值会踩坑) a = A() print(a.count) - 子类可继承访问
python
运行
class B(A): pass print(B.count) # 子类能读取父类类变量
2. 不可访问范围
- 不存在实例隔离:所有实例读取的是同一个类变量;
- 只有删除类本身,类变量才彻底失效。
四、作用域关键区别对照表
表格
| 对比项 | 实例变量 self.xxx | 类变量 |
|---|---|---|
| 归属载体 | 单个实例对象 | 类本身,所有实例共享 |
| 实例方法内 | ✅ self.xxx 直接访问 | ✅ self.var/ 类名.var |
| 类方法内 | ❌ 无法访问(无 self) | ✅ cls.var |
| 静态方法内 | ❌ 无法访问 | ✅ 类名.var |
| 类名直接访问 | ❌ 报错 | ✅ 推荐访问方式 |
| 跨实例访问 | ❌ A 实例不能读 B 实例变量 | ✅ 全部实例共用同一份 |
| 子类继承 | ❌ 不继承实例变量 | ✅ 子类可读取父类类变量 |
| 外部通过实例赋值 | 正常修改自身变量 | ❌ 不会改类变量,新建同名实例变量遮蔽 |
五、补充边界案例
1. 实例方法读取类变量没问题,但赋值有坑
python
运行
class A: num = 10 def func(self): print(self.num) # 读取:先找实例无,向上找类变量 ✅ self.num = 20 # 赋值:新增实例变量,不再操作类变量2. 类方法只能操作类变量,碰不到实例变量
python
运行
class A: num = 10 def __init__(self): self.age = 18 @classmethod def cls_func(cls): print(cls.num) # ✅ print(cls.age) # ❌ 报错,类看不到实例变量3. 子类修改类变量,不会污染父类作用域
python
运行
class Parent: val = 1 class Son(Parent): pass Son.val = 99 print(Parent.val) # 1,父类作用域不受影响六、一句话总结
- 实例变量作用域只局限于单个实例,只有拿到该实例对象才能访问,实例之间完全隔离;
- 类变量作用域覆盖整个类、所有实例、子类、全局代码,只要类存在,任何地方都能通过类名读取修改。