🧩 第四章 面向对象编程技术:去除冗余、进行整理
📄 一句话概括

重复 5 行降到 0 行、改一处从 2 处降到 1 处——继承不是复制代码,是共享一份代码。

📝 本节和前面几节的关系

4.8 讲同一句调用的公用化,这一节讲同一段代码的公用化。

一、最后一类重复

两个类里有一模一样的 __init__ 和 hello——这是抄,不是复用。 抄一遍就要改两处,回到 3.6 那个坑:漏一处就是一个隐藏的 bug。 更麻烦的是两份会慢慢长得不一样,因为改的人和时间都不同。

二、提到父类

Base 写一遍,A2(Base)、B2(Base) 各写一句 pass。 实测:重复的行从 5 行降到 0 行;改 hello 要动的地方从 2 处降到 1 处。 注意 pass 是很说明问题的:子类一个字没写,却什么都有。

三、B2 一个字没写为什么能用 hello

因为方法是顺着 MRO 往上找的(2.4 讲过)。 B2 自己没有 hello,就往上找到 Base 的——B2 的实例照样能调。 所以继承不是复制代码,是【共享一份代码】。 两者的差别在改的时候:复制要改 N 处,共享只改 1 处。

四、继承的边界

继承解决的是重复定义,不解决【长得像】。 两个类碰巧都有 name 字段,不代表它们该有共同父类。 判断标准:是不是【是一种】的关系?子类能不能替换父类而不出怪事? 用错了比不用更糟:父类一改,整个家族跟着变(2.4 讲的正是这件事)。

五、动手 Demo:三大要素之三——去除类的重复定义的继承

两个类各写一遍相同的初始化和方法,再改用继承写一遍。 数一数重复的行数,以及改一次要动几处。

# 不用继承:两个类各写一遍
class A1:
    def __init__(self, name): self.name = name
    def hello(self): return "我是 " + self.name
class B1:
    def __init__(self, name): self.name = name
    def hello(self): return "我是 " + self.name

# 用继承:只写一遍
class Base:
    def __init__(self, name): self.name = name
    def hello(self): return "我是 " + self.name
class A2(Base): pass
class B2(Base): pass

实跑输出:

--------------------------------------------------------------------
三大要素之三——去除类的重复定义的继承
--------------------------------------------------------------------

  两个类里有一模一样的代码——**这是最后一类重复,继承专治它。**

      不用继承:A1 和 B1 各写一遍 __init__ 和 hello
      用继承:  Base 写一遍,A2/B2 各写一句 pass

                        不用继承       用继承
  ----------------------------------------------
  类里重复的行          5 行           0 行
  改 hello 要动         2 处           1 处
  B 类还有 hello 吗     自己有一份     继承来的

  实测两个版本行为完全一致:**我是 甲/我是 乙**

  **注意最后一行**:B2 类里一个字都没写,但 `hello` 照样能用——
  **因为方法是顺着 MRO 往上找的(2.4 讲过)。**
  **所以继承不是复制代码,是【共享一份代码】。**

实测:重复的行从 5 行降到 0 行,改 hello 要动的地方从 2 处降到 1 处,两个版本行为完全一致。 B2 一个字没写也能用 hello——方法是顺着 MRO 往上找的,所以继承是共享一份代码,不是复制。

30 秒实验

  1. 找两个有相同方法的类,看它们是不是【是一种】的关系,再决定该不该抽父类。
  2. 改一次父类里的方法,看有多少子类受影响——这就是继承的代价面。

常见误区

  1. 以为继承是为了少写代码。它是为了只留一个能改的地方。
  2. 以为长得像就该继承。碰巧相同不等于是一种。
自测三问(点开对答案) 1. 继承治的是哪一类重复? —— 多个类里有一模一样的代码。实测重复行从 5 行降到 0 行,改动点从 2 处降到 1 处。 2. B2 什么都没写,为什么能用 hello? —— 方法是顺着 MRO 往上找的:B2 自己没有,就找到 Base 的,所以共享一份代码。 3. 继承的判断标准是什么? —— 是不是【是一种】的关系、子类能不能替换父类;碰巧相同不等于该继承,用错了父类一改全家受影响。

相关笔记