>>>PyPathPython 学习站
首页›核心篇›kp-017
核心篇 · Core lv.2 核心 kp-017

面向对象基础(class)

前置知识:kp-010、kp-015

1. 一句话定义

Python 的类是动态的命名空间:class 语句创建类对象,__init__ 初始化实例,方法只是"绑定到实例的普通函数",属性方法都存在字典里、随时可改。

2. 为什么重要

Python 中一切皆对象——连类本身也是对象(type 的实例)。理解"类是对象、方法是函数"这个朴素事实,能消除 OOP 的大部分神秘感,也是读懂框架源码的前提。

3. 前置知识

kp-010(dict——对象属性就是它)、kp-015(异常)。

4. 核心概念

python
class Book:
    shelf = "A-1"                      # 类属性:所有实例共享

    def __init__(self, title, price):
        self.title = title             # 实例属性:每个实例一份
        self.price = price

    def describe(self):                # 实例方法:自动接收 self
        return f"《{self.title}》 ¥{self.price}"

    @classmethod
    def from_json(cls, data):          # 类方法:接收 cls,常做替代构造器
        return cls(data["title"], data["price"])

    @staticmethod
    def is_valid_price(p):             # 静态方法:不接 self/cls 的工具函数
        return p >= 0

  • 三种方法:实例方法(self)、类方法(cls)、静态方法(无隐式参数)。
  • 属性即 dict:b.__dict__ 存实例属性;Book.__dict__ 存类成员。
  • 约定而非强制:_x 内部约定、__x 名字改写(name mangling),没有真正的 private。

5. 原理与机制

属性查找顺序:b.title → 先查 b.__dict__ → 再查 Book.__dict__ → 沿 MRO 上溯(kp-018);找不到 → __getattr__(若定义,kp-027)。

方法绑定:b.describe() 本质是 Book.describe(b)——函数通过描述符协议(kp-027)在访问时自动绑定第一个参数。理解这一点,self 不再是咒语。

6. 关键事实(模型/图示)

text
Book() 调用:
  __call__(type 级)──► __new__(Book) 创建实例 ──► __init__(实例) 填充
属性读:实例 __dict__ → 类 __dict__(沿 MRO)→ __getattr__
属性写:直接进实例 __dict__(可变对象天生开放)

7. 直观类比

类是图纸,实例是按图纸造出的产品;类属性是图纸上印的统一规格(所有产品一样),实例属性是每台机器出厂时填的序列号(各不相同)。Python 的特殊在于:图纸和产品都是实物(对象),你可以当场在图纸上加一行规格、在产品上贴一张新标签。

8. 实例与案例

python
# 类属性共享陷阱(kp-007 可变默认值的翻版)
class Team:
    members = []                 # ✗ 所有 Team 实例共享同一个列表!

t1, t2 = Team(), Team()
t1.members.append("a")           # t2.members 也多了 "a"

修复:在 __init__ 里 self.members = []。

9. 常见误区

  1. 把可变对象设为类属性 —— 上例;需要每实例独立的进 __init__。
  2. 以为 __x 是 private —— 只是名字改写为 _ClassName__x,外部仍可访问;这是"防止子类意外覆盖"的设施,不是访问控制。
  3. 方法里忘写 self 或调用时传 self —— 定义带 self、调用不传;b.describe() 不要写成 b.describe(b)。
  4. 什么都塞进类 —— Python 支持"一个模块 + 一堆函数"的扁平风格;没有状态就别硬造类(YAGNI)。

10. 自测题

  1. Book.from_json 为什么要用 @classmethod 而不是普通方法?
  2. b = Book("x", 1),b.describe() 的等价调用形式?
  3. 类属性与实例属性查找顺序?
参考答案
  1. 类方法接收 cls,子类调用时自动传子类,替代构造器能正确返回子类实例。
  2. Book.describe(b)。
  3. 先实例 __dict__,再类(沿 MRO);都未命中则触发 __getattr__。

11. 与其他知识点的关系

  • kp-018 继承与 MRO:组合与多继承。
  • kp-019 魔术方法:让自定义类接入语言协议。
  • kp-020 dataclass:消除样板代码。

12. 延伸阅读

  • Python 官方教程 §9 Classes