Python 类属性与实例属性
说明 Python 中类属性与实例属性分别存放在哪里、如何查找、何时共享,以及为什么同名赋值会发生遮蔽。
#type / concept
#status / growing
#resource / python
#tech / lang / python
[!info] related notes
- 所属 MOC: Python 类与面向对象 MOC
- 前置概念: python类与面向对象
- 并列概念: python实例方法类方法与静态方法
- 易混淆概念: python继承方法重写与super
- 关系笔记: python类与面向对象
Python 类属性与实例属性
一句话定义
类属性属于类对象本身,实例属性属于某个具体实例;读取属性时 Python 会按查找链逐层找,写入属性时则通常只改当前对象自己的命名空间。
核心机制 / 工作原理
1. 类和实例各自有自己的命名空间
class语句执行后会创建一个类对象,类体里的名字进入类命名空间。obj = MyClass()创建实例后,实例自己的状态通常放在obj.__dict__里。- 因此“类属性”和“实例属性”不是抽象分类,而是实际存放在两个不同对象上的名字。
2. 读取属性时会走查找链
对 obj.x 来说,最常见的查找顺序是:
- 先找实例自己的属性
- 再找类上的属性
- 再按基类的 MRO 继续向上找
这也是为什么实例可以“读到”类属性,但并不代表它把类属性复制了一份。
3. 写入同名属性通常会发生遮蔽
如果执行 obj.x = ...,大多数情况下 Python 会把 x 写到实例自己的命名空间里,于是它会遮蔽类上的同名属性。之后:
obj.x读到的是实例自己的值MyClass.x仍然保持原来的类属性值
4. 类属性适合共享默认值和类级元数据
类属性常用于:
- 常量或配置默认值
- 所有实例共享的计数器
- 描述这个类本身的元信息
不适合把“每个实例独有的可变状态”放成类属性。
最小例子 / 最小场景
class User:
role = "member" # 类属性
def __init__(self, name: str):
self.name = name # 实例属性
u1 = User("Alice")
u2 = User("Bob")
print(u1.role) # member,来自类属性
print(User.role) # member
u1.role = "admin" # 给 u1 新增了同名实例属性
print(u1.role) # admin
print(u2.role) # member
print(User.role) # member
这个例子里,u1.role = "admin" 并没有改掉类属性,而是在 u1 身上创建了一个新的实例属性。
边界与易混淆点
- 通过实例读取到类属性,不等于“实例拥有这份数据”;它只是查找时向类对象回退了。
- 可变类属性是最常见陷阱之一,例如把
[]、{}放成类属性,所有实例会共享同一份对象。 - 如果你想给每个实例一份独立状态,应在
__init__里写self.x = ...,而不是在类体里写可变默认值。