🧩 第五章 理解内存结构:程序员的基本素养
📄 一句话概括

同一个变量名指向不同类的实例——调用点一个字没改,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 秒实验

  1. 往列表里再加一个你写的类,看调用点要不要改。
  2. 用 type(s).__name__ 打印每轮的类名,直观看到变量在换物体。

常见误区

  1. 以为多态是编译器识别的。多数语言是运行期查表。
  2. 以为看起来一样就是同一个类。类不同,方法名相同。
自测三问(点开对答案) 1. 多态在内存里是什么? —— 同一个变量名先后指向不同类的实例;每个对象带着指向自己类的指针,所以能找对方法。 2. 实测两个对象各落到哪? —— Rect 落到 Rect.area 得 24.00,Circle 落到 Circle.area 得 28.27,合计 52.27431。 3. 它和 4.8 的关系是什么? —— 4.8 讲结果(调用端公用化),这里讲原因(变量存地址、对象带类指针、类带方法表)。

相关笔记