Python 类属性与实例属性

说明 Python 中类属性与实例属性分别存放在哪里、如何查找、何时共享,以及为什么同名赋值会发生遮蔽。

#type / concept #status / growing #resource / python #tech / lang / python

[!info] related notes

Python 类属性与实例属性

一句话定义

类属性属于类对象本身,实例属性属于某个具体实例;读取属性时 Python 会按查找链逐层找,写入属性时则通常只改当前对象自己的命名空间。

核心机制 / 工作原理

1. 类和实例各自有自己的命名空间

  • class 语句执行后会创建一个类对象,类体里的名字进入类命名空间。
  • obj = MyClass() 创建实例后,实例自己的状态通常放在 obj.__dict__ 里。
  • 因此“类属性”和“实例属性”不是抽象分类,而是实际存放在两个不同对象上的名字。

2. 读取属性时会走查找链

obj.x 来说,最常见的查找顺序是:

  1. 先找实例自己的属性
  2. 再找类上的属性
  3. 再按基类的 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 身上创建了一个新的实例属性。

边界与易混淆点

  1. 通过实例读取到类属性,不等于“实例拥有这份数据”;它只是查找时向类对象回退了。
  2. 可变类属性是最常见陷阱之一,例如把 []{} 放成类属性,所有实例会共享同一份对象。
  3. 如果你想给每个实例一份独立状态,应在 __init__ 里写 self.x = ...,而不是在类体里写可变默认值。
创建于 2026/5/20 更新于 2026/5/27