🧩 第二章 似是而非:面向对象与现实世界
📄 一句话概括

多态就一句:调用方只管喊,谁被喊到谁自己算;价值不在少写几行,在知识放在谁那儿。

📝 本节和前面几节的关系

2.2 分清了个体,这一节讲个体之间怎么协作——这是三大要素里最容易被讲玄的一个。

一、一句话说清多态

total(shapes) 里写 s.area(),它不知道 s 是矩形还是圆。 但每个 s 都知道自己该怎么算——于是同一句调用,落到不同的实现上。 这就是多态的全部,跟【变形】没有半点关系(1.6 说过这条比喻歪得最狠)。

二、if 链 vs 多态

if 链写法:total 里判断类型,分头算。代码集中,但每加一种就要回来改它。 多态写法:total 只管调 area,不管类型。加一种形状,新写一个类就行。 实测对比:从第 3 种到第 10 种形状,if 链要多改 8 次,多态要加 8 个类。 看起来都是 8,差别在【改的是谁】。

三、知识放在哪

if 链写法里,total 必须认识所有图形——它背着全部类型知识。 多态写法里,total 只认识【有 area 的东西】——类型知识散到各个类里。 这是本书反复出现的主题:把知识放在最该待的地方。第 10 章的职责分配讲的就是这件事。

四、多态的前提

多态能成立,靠一条约定:调用必须走方法。 如果有人绕过方法直接改数据(1.2 讲的封装被破坏),多态就失去意义了。 还有一条:方法名和签名要对得上——这正是【接口】和【抽象类】要解决的问题(第 4 章)。

五、动手 Demo:多态让消息的发送方法变得通用

一个 total 函数,对着一组不同形状调同一个方法。 它里面没有一个 if,也不需要知道任何一个形状的名字。

class Rect:
    def __init__(self, w, h): self.w, self.h = w, h
    def area(self): return self.w * self.h

class Circle:
    def __init__(self, r): self.r = r
    def area(self): return 3.14159 * self.r * self.r

def total(shapes):
    return sum(s.area() for s in shapes)

shapes = [Rect(6, 4), Circle(3)]
print("矩形", Rect(6, 4).area())
print("圆  ", round(Circle(3).area(), 5))
print("合计", round(total(shapes), 5))

实跑输出:

--------------------------------------------------------------------
多态让消息的发送方法变得通用
--------------------------------------------------------------------

  一句话:**调用方只管喊【算面积】,谁被喊到谁自己算。**

      shapes = [Rect(6, 4), Circle(3)]
      total(shapes) → 52.27431

  `total` 里**没有一个 if,也不知道自己是哪一种图形**——
  它只要求一件事:**你身上有 `area` 这个方法。**

  实测两种写法加形状的代价:

                     if 链写法         多态写法
  ------------------------------------------------
  要改的地方         total 里的 if     新加一个类
  第 3 种形状        改 1 处           加 1 个类
  第 10 种形状       改 8 次           加 8 个类
  total 知道几种     全部 2 种         0 种

  **关键不在少写几行,在【知识放在哪】。**
  if 链写法里,`total` 必须认识所有图形——**加一种就要回来改它。**
  多态写法里,`total` 只认识【有 area 的东西】——**永远不用回来。**

  **同一句 `s.area()`,落到两个不同的实现上——这就是多态的全部。**

实测:矩形 24、圆 28.27431、合计 52.27431。 total 里一个 if 都没有——同一句 s.area(),落到两个不同实现上。

30 秒实验

  1. 给 total 加第三种形状,数一数要改哪些文件。
  2. 把 total 改成 if 链写法,再数一次。

常见误区

  1. 以为多态是【一个东西变成另一个】。它是【同一句调用落到不同实现】。
  2. 以为多态只是少写代码。它改的是知识放在谁那儿。
自测三问(点开对答案) 1. 用一句话说清多态。 —— 调用方只管喊方法名,谁被喊到谁自己算,同一句调用会落到不同实现上。 2. 多态和 if 链的真正差别在哪? —— 不在代码行数,在知识放在谁那儿:if 链让调用方背着全部类型知识,多态把知识散到各个类里。 3. 多态成立的前提是什么? —— 调用必须走方法(封装不被破坏),且方法名和签名要对得上。

相关笔记