Skip to content

继承基础 ​

定义老虎、狮子和猫时,它们都有名称、身高、体重,也都有吃东西的行为。如果每个类都重复编写这些属性和方法,代码会越来越长,修改时也容易漏掉某个类。如何复用这些共同内容,同时保留每种动物自己的行为?

继承可以把共同内容放进一个父类,再让其他类直接使用。本节先学习单继承:一个子类直接继承一个父类,并逐步扩展子类内容和继承层级。

单继承关系 ​

当多个类具有共同的属性和行为时,如何只定义一次?继承可以让子类直接获得父类已经定义的属性和方法,再补充自己的内容。

  • 被继承的类叫父类
  • 继承其他类的类叫子类

基本语法如下:

python
class 子类名(父类名):
    pass

下面定义一个英雄类,再让坦克英雄继承它:

Hero 与 Tank 继承关系

python
class Hero:
    def __init__(self, name):
        self.name = name

    def hello(self):
        print(f'{self.name} 正在向你打招呼')


class Tank(Hero):
    pass


tank = Tank('坦克')
print(tank.name)
tank.hello()

运行结果:

text
坦克
坦克 正在向你打招呼

Tank 自己没有编写 __init__() 和 hello(),但可以直接使用父类 Hero 中的内容。

继承通常用来表达“子类是父类的一种”。例如,坦克英雄是一种英雄,老虎是一种动物,这些关系适合使用继承。

继承和对象协作不同

上一章的玩家会使用骰子,但玩家不是一种骰子,所以 Player 不应该继承 Dice。两个对象只是互相配合时,直接调用对方提供的方法即可。

is-a 继承与 has-a 组合对照

父类属性初始化 ​

如果子类需要增加自己的实例属性,如何同时完成父类和子类的初始化?可以编写自己的 __init__(),再使用 super() 调用父类的初始化方法:

python
class Hero:
    def __init__(self, name):
        self.name = name

    def hello(self):
        print(f'{self.name} 正在向你打招呼')


class Tank(Hero):
    def __init__(self, name, defence, blood):
        super().__init__(name)
        self.defence = defence
        self.blood = blood


tank = Tank('坦克', 100, 1000)
print(tank.name, tank.defence, tank.blood)
tank.hello()

super().__init__(name) 会调用父类的 __init__(),完成 name 的初始化。后面两行再初始化坦克英雄特有的 defence 和 blood。

执行顺序可以概括为:

  1. 创建 Tank 对象
  2. 进入 Tank.__init__()
  3. 通过 super().__init__(name) 初始化父类属性
  4. 继续初始化子类自己的属性

super 初始化父类和子类属性的执行顺序

如果子类编写了 __init__(),却没有初始化父类需要的属性,后面访问这些属性时可能触发 AttributeError。

案例:动物类继承设计 ​

需求:定义 Animal、Tiger 和 Lion,让两个子类复用公共属性与 eat(),同时保留各自的叫声和技能。

功能分析:先观察不使用继承时的重复代码,再改用 Animal 作为父类,比较两种结构的维护差异。

具体结构如下:

text
动物(Animal)
    属性:name、high、weight
    方法:eat

老虎(Tiger)
    公共内容:name、high、weight、eat
    特有方法:bark、tiger_skill

狮子(Lion)
    公共内容:name、high、weight、eat
    特有方法:bark、lion_skill

独立类实现 ​

如果每个类都独立编写,公共属性和 eat() 会重复出现:

无继承类结构

python
class Animal:
    def __init__(self, name, high, weight):
        self.name = name
        self.high = high
        self.weight = weight

    def eat(self):
        print(f'{self.name} 正在吃东西')


class Tiger:
    def __init__(self, name, high, weight):
        self.name = name
        self.high = high
        self.weight = weight

    def eat(self):
        print(f'{self.name} 正在吃东西')

    def bark(self):
        print(f'{self.name} 正在嗷呜叫')

    def tiger_skill(self):
        print(f'{self.name} 使用老虎技能')


class Lion:
    def __init__(self, name, high, weight):
        self.name = name
        self.high = high
        self.weight = weight

    def eat(self):
        print(f'{self.name} 正在吃东西')

    def bark(self):
        print(f'{self.name} 正在吼叫')

    def lion_skill(self):
        print(f'{self.name} 使用狮子技能')

name、high、weight 和 eat() 在三个类中重复出现。以后修改公共属性或吃东西的行为时,三个类都可能需要调整。

继承实现 ​

使用继承后,可以把公共内容放在 Animal 中,让 Tiger 和 Lion 只保存自己的特有行为:

使用继承后的类结构

python
class Animal:
    def __init__(self, name, high, weight):
        self.name = name
        self.high = high
        self.weight = weight

    def eat(self):
        print(f'{self.name} 正在吃东西')


class Tiger(Animal):
    def __init__(self, name, high, weight):
        super().__init__(name, high, weight)

    def bark(self):
        print(f'{self.name} 正在嗷呜叫')

    def tiger_skill(self):
        print(f'{self.name} 使用老虎技能')


class Lion(Animal):
    def __init__(self, name, high, weight):
        super().__init__(name, high, weight)

    def bark(self):
        print(f'{self.name} 正在吼叫')

    def lion_skill(self):
        print(f'{self.name} 使用狮子技能')


tiger = Tiger('泰哥', 1.88, 360)
lion = Lion('辛巴', 2, 380)

tiger.eat()
tiger.bark()
tiger.tiger_skill()

lion.eat()
lion.bark()
lion.lion_skill()

运行结果:

text
泰哥 正在吃东西
泰哥 正在嗷呜叫
泰哥 使用老虎技能
辛巴 正在吃东西
辛巴 正在吼叫
辛巴 使用狮子技能

现在,公共属性和 eat() 只需要在 Animal 中定义一次。两个子类继承公共内容,再分别增加自己的叫声和技能。

子类扩展 ​

子类不仅可以使用父类的内容,还可以增加自己的实例属性和方法。如何在不修改父类的前提下,为 Cat 增加毛色、叫声和信息展示功能?

继续在上一个示例中添加 Cat,让它继承 Animal,并增加毛色、叫声和信息展示功能:

Cat 子类扩展模型

python
class Cat(Animal):
    def __init__(self, name, high, weight, color):
        super().__init__(name, high, weight)
        self.color = color

    def meow(self):
        print(f'{self.name} 正在喵喵叫')

    def show_info(self):
        print(
            f'名字:{self.name},毛色:{self.color},'
            f'身高:{self.high},体重:{self.weight}'
        )


cat = Cat('小猫咪', 0.3, 4, '银白色')
cat.eat()
cat.meow()
cat.show_info()

这里可以分成两部分看:

  • name、high、weight 和 eat() 来自父类 Animal
  • color、meow() 和 show_info() 由子类 Cat 自己定义

本节只让子类增加新内容。子类修改父类已有方法的写法,会在“方法重写”一节学习。

多层继承关系 ​

一个子类还可以继续成为其他类的父类。继承层级增加后,实例可以沿继承链获得哪些方法?例如,Jiao 继承 Snake,Dragon 再继承 Jiao:

蛇到蛟再到龙的继承链

python
class Snake:
    def __init__(self, name, color, length, size):
        self.name = name
        self.color = color
        self.length = length
        self.size = size

    def run(self):
        return f'{self.name} 正在向前爬行'


class Jiao(Snake):
    def __init__(self, name, color, length, size, claw, corner):
        super().__init__(name, color, length, size)
        self.claw = claw
        self.corner = corner

    def fly(self):
        return f'{self.name} 正在腾云驾雾'


class Dragon(Jiao):
    def skill(self):
        return f'{self.name} 正在施云布雨'


snake = Snake('小花蛇', '斑纹', '50 cm', '5 cm')
print(snake.run())

jiao = Jiao('小花蛟', '斑纹', '250 cm', '50 cm', 2, 1)
print(jiao.run())
print(jiao.fly())

dragon = Dragon('小金龙', '金色', '2500 cm', '250 cm', 5, 2)
print(dragon.run())
print(dragon.fly())
print(dragon.skill())

Dragon 可以使用三个层级中的内容:

  1. 从 Snake 间接继承 run()
  2. 从 Jiao 继承 fly()
  3. 使用自己定义的 skill()

这仍然是单继承:Jiao 直接继承一个父类,Dragon 也只直接继承一个父类。一个子类同时继承多个父类的情况,会在下一节学习。

小结 ​

本节重点掌握四件事:

  1. 使用 class 子类(父类) 建立继承关系
  2. 子类可以直接使用父类已有的属性和方法
  3. 子类通过 super().__init__() 初始化父类属性
  4. 子类可以增加自己的属性和方法,也可以继续成为其他类的父类

练习 ​

  1. 定义一个 Vehicle 父类,添加 brand 属性和 run() 方法
  2. 定义 Car 和 Bike 两个子类,分别增加一个特有属性和方法
  3. 创建子类对象,分别调用继承的方法和自己新增的方法
  4. 给 Car 增加自己的 __init__(),使用 super().__init__() 初始化 brand