📄 一句话概括
继承只做两件事:共同点往上提、不同点往下放;子类没写的方法是顺着链往上找到的,不是复制的。
📝 本节和前面几节的关系
2.3 讲横向协作,这一节讲纵向分类——三大要素里的第三根柱子。
一、上提与下放
矩形和圆都叫【形状】,都有一个名字——这就是共同点,提到 Shape 里写一遍。
面积算法不一样——这就是不同点,下放到各自的 area 里。
继承的动作就这么两个,没有第三个。
二、MRO:找方法的顺序
实测 Rect.__mro__ 是 Rect → Shape → object。
这说明:子类没写的方法,是顺着这条链往上找到的,不是复制一份下来的。
所以 Rect().describe() 能跑——它用的是 Shape 那份。
这一条直接决定了下一个结论。
三、改父类,子类一起变
因为方法是【往上找】的,改 Shape.describe,所有子类同时变。
好处是省事,坏处是危险——父类一动,所有子类的行为都跟着动,可能谁都没意识到。
这就是为什么很多团队规定【父类尽量稳】——动它等于动全家族。
四、继承 vs 组合
继承说的是【是一种】(is-a):矩形是一种形状。 组合说的是【有一个】(has-a):订单有一个顾客。 最容易搞错的一条:只想复用几行代码,别用继承——用组合。 因为继承会把两个类的生命周期绑在一起,而你可能只是想要那几行代码。
五、动手 Demo:继承对共同点和不同点进行系统的分类和整理
把共同点提到父类、不同点留在子类,再看 Python 找方法的顺序。 MRO 会告诉我们:子类没写的方法到底是从哪儿来的。
class Shape:
def __init__(self, name): self.name = name
def describe(self): return self.name
class Rect(Shape):
def __init__(self, w, h):
Shape.__init__(self, "矩形")
self.w, self.h = w, h
def area(self): return self.w * self.h
class Circle(Shape):
def __init__(self, r):
Shape.__init__(self, "圆")
self.r = r
def area(self): return 3.14159 * self.r * self.r
for c in (Rect, Circle):
print(c.__name__, [x.__name__ for x in c.__mro__])
print(Rect(6, 4).describe(), Circle(3).describe())
实跑输出:
--------------------------------------------------------------------
继承对共同点和不同点进行系统的分类和整理
--------------------------------------------------------------------
继承的动作只有两个:**把共同点往上提,把不同点往下放。**
class Shape: name / describe() ← 共同点
class Rect: area() = w * h ← 不同点
class Circle: area() = π * r * r ← 不同点
实测方法解析顺序(MRO)——Python 找一个方法时按什么顺序翻:
Rect → Rect → Shape → object
Circle → Circle → Shape → object
这解释了一条常被忽略的事:**子类没写的方法,是【顺着链路往上找】找到的**,
不是【复制一份下来】。**所以改父类的方法,所有子类一起变。**
实测确认一下这个【一起变】:
Rect().describe() → 矩形
Circle().describe() → 圆
两个子类都没写 `describe`,**是 Shape 那份在起作用。**
继承 组合
--------------------------------------------------
关系说法 是一种(is-a) 有一个(has-a)
改父类影响 所有子类跟着变 只影响自己
什么时候用 共同点真一样 只想复用点代码
**最后一行最容易搞错**:只想复用几行代码,别用继承——用组合。
实测 MRO:Rect → Shape → object、Circle → Shape → object。
两个子类都没写 describe,是 Shape 那份在起作用——方法是往上找的,不是复制的。
30 秒实验
- 在父类里改一句
describe,看两个子类的输出是不是一起变。 - 把继承改成组合,看看代码会多几行、好处在哪。
常见误区
- 以为继承是把父类代码复制下来。是顺着链往上找。
- 为了复用几行代码就用继承。那是组合的活儿。
自测三问(点开对答案)
1. 继承只做哪两件事? —— 把共同点往上提,把不同点往下放。 2. MRO 说明了什么? —— 子类没写的方法不是复制下来的,而是顺着 Rect → Shape → object 这条链往上找到的。 3. 什么时候不该用继承? —— 只是想复用几行代码时——用组合;继承会绑住两个类的生命周期。- ☐ 下一节(2.5 使用比喻进行讲解容易造成混乱)
相关笔记
- 多态让消息的发送方法变得通用 — 横向协作
- 面向对象和现实世界是似是而非的 — 继承最容易歪的地方
- ☕ Java基础 · 索引 — 继承与接口的语法
- 🧩 面向对象是怎样工作的 · 索引