继承基础
定义老虎、狮子和猫时,它们都有名称、身高、体重,也都有吃东西的行为。如果每个类都重复编写这些属性和方法,代码会越来越长,修改时也容易漏掉某个类。如何复用这些共同内容,同时保留每种动物自己的行为?
继承可以把共同内容放进一个父类,再让其他类直接使用。本节先学习单继承:一个子类直接继承一个父类,并逐步扩展子类内容和继承层级。
单继承关系
当多个类具有共同的属性和行为时,如何只定义一次?继承可以让子类直接获得父类已经定义的属性和方法,再补充自己的内容。
- 被继承的类叫父类
- 继承其他类的类叫子类
基本语法如下:
class 子类名(父类名):
pass下面定义一个英雄类,再让坦克英雄继承它:
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()运行结果:
坦克
坦克 正在向你打招呼Tank 自己没有编写 __init__() 和 hello(),但可以直接使用父类 Hero 中的内容。
继承通常用来表达“子类是父类的一种”。例如,坦克英雄是一种英雄,老虎是一种动物,这些关系适合使用继承。
继承和对象协作不同
上一章的玩家会使用骰子,但玩家不是一种骰子,所以 Player 不应该继承 Dice。两个对象只是互相配合时,直接调用对方提供的方法即可。
父类属性初始化
如果子类需要增加自己的实例属性,如何同时完成父类和子类的初始化?可以编写自己的 __init__(),再使用 super() 调用父类的初始化方法:
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。
执行顺序可以概括为:
- 创建
Tank对象 - 进入
Tank.__init__() - 通过
super().__init__(name)初始化父类属性 - 继续初始化子类自己的属性
如果子类编写了 __init__(),却没有初始化父类需要的属性,后面访问这些属性时可能触发 AttributeError。
案例:动物类继承设计
需求:定义 Animal、Tiger 和 Lion,让两个子类复用公共属性与 eat(),同时保留各自的叫声和技能。
功能分析:先观察不使用继承时的重复代码,再改用 Animal 作为父类,比较两种结构的维护差异。
具体结构如下:
动物(Animal)
属性:name、high、weight
方法:eat
老虎(Tiger)
公共内容:name、high、weight、eat
特有方法:bark、tiger_skill
狮子(Lion)
公共内容:name、high、weight、eat
特有方法:bark、lion_skill独立类实现
如果每个类都独立编写,公共属性和 eat() 会重复出现:
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 只保存自己的特有行为:
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()运行结果:
泰哥 正在吃东西
泰哥 正在嗷呜叫
泰哥 使用老虎技能
辛巴 正在吃东西
辛巴 正在吼叫
辛巴 使用狮子技能现在,公共属性和 eat() 只需要在 Animal 中定义一次。两个子类继承公共内容,再分别增加自己的叫声和技能。
子类扩展
子类不仅可以使用父类的内容,还可以增加自己的实例属性和方法。如何在不修改父类的前提下,为 Cat 增加毛色、叫声和信息展示功能?
继续在上一个示例中添加 Cat,让它继承 Animal,并增加毛色、叫声和信息展示功能:
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()来自父类Animalcolor、meow()和show_info()由子类Cat自己定义
本节只让子类增加新内容。子类修改父类已有方法的写法,会在“方法重写”一节学习。
多层继承关系
一个子类还可以继续成为其他类的父类。继承层级增加后,实例可以沿继承链获得哪些方法?例如,Jiao 继承 Snake,Dragon 再继承 Jiao:
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 可以使用三个层级中的内容:
- 从
Snake间接继承run() - 从
Jiao继承fly() - 使用自己定义的
skill()
这仍然是单继承:Jiao 直接继承一个父类,Dragon 也只直接继承一个父类。一个子类同时继承多个父类的情况,会在下一节学习。
小结
本节重点掌握四件事:
- 使用
class 子类(父类)建立继承关系 - 子类可以直接使用父类已有的属性和方法
- 子类通过
super().__init__()初始化父类属性 - 子类可以增加自己的属性和方法,也可以继续成为其他类的父类
练习
- 定义一个
Vehicle父类,添加brand属性和run()方法 - 定义
Car和Bike两个子类,分别增加一个特有属性和方法 - 创建子类对象,分别调用继承的方法和自己新增的方法
- 给
Car增加自己的__init__(),使用super().__init__()初始化brand