📄 一句话概括
同一个变量名指向不同类的实例——调用点一个字没改,Rect.area 和 Circle.area 各算各的,合计 52.27431。
📝 本节和前面几节的关系
5.9~5.10 讲指针和复制,这一节把它们拼起来:多态在内存里长什么样。
一、内存视角
多态在内存里就是一件事:同一个变量名,先后指向了不同类的实例。
shapes = [Rect(6, 4), Circle(3)]——列表里装的是两个地址,指向两块不同的堆。
而它们各自身上都挂着一个指向自己类的指针,所以能找对方法。
二、实测
第一个对象:类型 Rect,方法来自 Rect.area,面积 24.00。
第二个对象:类型 Circle,方法来自 Circle.area,面积 28.27。
同一句 s.area() 合计 52.27431——调用点一个字没变。
两个对象的类相同吗?不同。这一句就是多态的全部。
三、看起来一样的机制
对象里带一个指向类的指针(5.7 说的那份唯一类信息)。 类里带一张方法表,同名方法各占一格。 调用时先看对象的类,再去那张表里取——取到谁就是谁。 所以【看起来一样】不是眼睛看出来的,是运行期查表查出来的。
四、和 4.8 对上
4.8 讲的是结果:调用端公用化,加形状不用改调用方。
这一节讲的是原因:凭什么一句 s.area() 能落到两份不同的代码上。
一句话串起来:变量存地址(5.9)、对象带类指针(5.7)、类带方法表——于是同一句话可以有 N 种后果。
五、动手 Demo:多态让不同的类看起来一样
一个列表装两个不同类的实例,调同一个方法名。 看它们各自落到哪份代码上。
shapes = [Rect(6, 4), Circle(3)]
for s in shapes:
print(type(s).__name__, "%s.area" % type(s).__name__, s.area())
print(sum(s.area() for s in shapes))
实跑输出:
--------------------------------------------------------------------
多态让不同的类看起来一样
--------------------------------------------------------------------
从内存角度看多态,**就是【同一个变量名,指向了不同类的实例】。**实测:
对象 它的类 它的方法来自 面积
--------------------------------------------------------
实例 Rect Rect.area 24.00
实例 Circle Circle.area 28.27
同一句 s.area() 合计 52.27431
两个对象的类相同吗 不同
**调用点那一句 `s.area()` 一个字没变,变的只是 s 里存的那个地址。**
**这就是【看起来一样】:调用方看到的是同一个方法名,背后是两份不同的代码。**
**实现办法在 5.7~5.9 已经齐了:对象里带一个指向自己类的指针,顺它找方法。**
实测:一个落到 Rect.area(24.00)、一个落到 Circle.area(28.27),合计 52.27431。 调用点一个字没变,变的只是 s 里存的地址——这就是【看起来一样】的全部真相。
30 秒实验
- 往列表里再加一个你写的类,看调用点要不要改。
- 用
type(s).__name__打印每轮的类名,直观看到变量在换物体。
常见误区
- 以为多态是编译器识别的。多数语言是运行期查表。
- 以为看起来一样就是同一个类。类不同,方法名相同。
自测三问(点开对答案)
1. 多态在内存里是什么? —— 同一个变量名先后指向不同类的实例;每个对象带着指向自己类的指针,所以能找对方法。 2. 实测两个对象各落到哪? —— Rect 落到 Rect.area 得 24.00,Circle 落到 Circle.area 得 28.27,合计 52.27431。 3. 它和 4.8 的关系是什么? —— 4.8 讲结果(调用端公用化),这里讲原因(变量存地址、对象带类指针、类带方法表)。- ☐ 下一节(5.12 根据继承的信息类型的不同,内存配置也不同)
相关笔记
- 三大要素之二:实现调用端公用化的多态 — 同一件事的结果面
- 每个类只加载一个类信息 — 对象带的那个指针
- 根据继承的信息类型的不同,内存配置也不同 — 布局还能再改
- 🧩 面向对象是怎样工作的 · 索引