零基础学 Python 第16章 | 继承与方法重写
本章摘要
- 核心目标:掌握单继承、
super()、方法重写与多继承,能用继承复用代码、用重写定制行为 - 核心知识点:单继承语法、
super()调用父类、方法重写、多继承、MRO方法解析顺序 - 实操产出:动物王国继承体系(
Animal基类+Dog/Cat/Bird子类) - 前置基础:第15章类与对象(
class定义、__init__、实例方法)
一、写在前面
上一章你学会了定义类、创建对象、写 __init__ 和实例方法。现在设想一个场景:你要写 Dog 类和 Cat 类,它们都有 name、age 属性,都有 eat 方法。如果两个类各写一遍,代码重复,改一个忘改另一个,bug 就来了。
继承就是解决这个问题——子类直接获得父类的属性和方法,不用重写一遍。子类还可以重写(覆盖)父类的方法来定制自己的行为,也可以新增自己的方法。这一章就来学继承的完整用法。
二、什么是继承
一句话说清楚:子类继承父类,自动拥有父类的所有属性和方法,同时可以扩展自己的。
生活类比:父类是"动物",有名字、年龄,能吃东西。子类是"狗",自动拥有动物的一切,还能加自己的——汪汪叫、摇尾巴。
先看一个最简单的例子:
# 父类
class Animal:
def __init__(self, name):
self.name = name
def eat(self):
print(f"{self.name}在吃东西")
# 子类继承父类(括号里写父类名)
class Dog(Animal):
pass # 什么都不写,也能用父类的一切
dog = Dog("旺财")
dog.eat() # 旺财在吃东西 ← 继承来的,Dog自己没写 eat
Dog(Animal) 中的括号就是继承的标志。Dog 什么都没写(pass),但创建 Dog("旺财") 后,dog.eat() 照样能用——因为 eat 是从 Animal 继承来的。
三、单继承语法
3.1 基本语法
class 子类名(父类名):
# 子类自己的属性和方法
就这一行语法,记住就好。如果类定义时不写括号(class Dog:),默认继承 Python 的根类 object,所有类最终都继承自 object。
3.2 子类拥有父类的一切
子类自动获得父类的属性和方法,不需要重新定义:
class Animal:
def __init__(self, name, age):
self.name = name
self.age = age
def eat(self):
print(f"{self.name}在吃东西")
def sleep(self):
print(f"{self.name}在睡觉")
class Dog(Animal):
pass
dog = Dog("旺财", 3)
print(dog.name) # 旺财 ← 父类的属性
print(dog.age) # 3 ← 父类的属性
dog.eat() # 旺财在吃东西 ← 父类的方法
dog.sleep() # 旺财在睡觉 ← 父类的方法
3.3 子类扩展自己的方法
继承不是只能"白拿",子类可以添加父类没有的方法:
class Animal:
def __init__(self, name):
self.name = name
def eat(self):
print(f"{self.name}在吃东西")
class Dog(Animal):
# Dog 独有的方法
def bark(self):
print(f"{self.name}汪汪叫")
def wag_tail(self):
print(f"{self.name}摇尾巴")
dog = Dog("旺财")
dog.eat() # 旺财在吃东西 ← 继承自 Animal
dog.bark() # 旺财汪汪叫 ← Dog 自己的
dog.wag_tail() # 旺财摇尾巴 ← Dog 自己的
子类 = 父类的一切 + 自己新增的内容。能用的更多了,不会更少。
四、子类调用父类方法:super()
4.1 为什么需要 super()
当子类需要自己的 __init__ 时(比如 Dog 比 Animal 多一个 breed 属性),会覆盖父类的 __init__。这时父类的属性就不会被初始化了:
class Animal:
def __init__(self, name, age):
self.name = name
self.age = age
class Dog(Animal):
def __init__(self, name, age, breed):
# ❌ 忘记调用 super().__init__(),父类属性没有初始化
self.breed = breed
dog = Dog("旺财", 3, "金毛")
print(dog.breed) # 金毛 ← 子类属性正常
print(dog.name) # ❌ AttributeError: 'Dog' object has no attribute 'name'
子类定义了 __init__,就把父类的 __init__ 覆盖了。self.name 和 self.age 根本没被赋值,访问就报错。解决办法就是用 super() 调用父类的 __init__。
4.2 super() 调用父类 __init__
class Animal:
def __init__(self, name, age):
self.name = name
self.age = age
class Dog(Animal):
def __init__(self, name, age, breed):
super().__init__(name, age) # ✅ 调用父类 __init__,初始化 name 和 age
self.breed = breed # 子类自己的属性
dog = Dog("旺财", 3, "金毛")
print(dog.name) # 旺财 ← 父类初始化的
print(dog.age) # 3 ← 父类初始化的
print(dog.breed) # 金毛 ← 子类自己的
super().__init__(name, age) 的意思是:先让父类把 name 和 age 初始化好,子类再添加自己的 breed。执行顺序是"父类先,子类后"。
4.3 super() 调用父类普通方法
super() 不限于 __init__,父类的任何方法都可以用 super() 调用:
class Animal:
def eat(self):
print(f"{self.name}在吃东西")
class Dog(Animal):
def eat(self):
super().eat() # 先执行父类的 eat
print(f"{self.name}吃完了,舔了舔嘴巴") # 再添加自己的行为
dog = Dog("旺财", 3, "金毛")
dog.eat()
# 旺财在吃东西
# 旺财吃完了,舔了舔嘴巴
super()的本质:在子类方法中,"顺便"调用一下父类的同名方法。父类的逻辑先跑,子类的逻辑接着跑,两者都不丢。
五、方法重写
5.1 重写父类方法
方法重写(Override):子类中定义一个与父类同名的方法,调用时执行子类的版本,父类的被覆盖。
class Animal:
def speak(self):
print("动物发出声音")
class Dog(Animal):
def speak(self):
print("汪汪汪!") # 重写:覆盖父类的 speak
class Cat(Animal):
def speak(self):
print("喵喵喵!") # 重写:覆盖父类的 speak
dog = Dog()
cat = Cat()
dog.speak() # 汪汪汪!← 执行 Dog 的版本
cat.speak() # 喵喵喵!← 执行 Cat 的版本
同一个 speak() 方法,Dog 对象调用就汪汪叫,Cat 对象调用就喵喵叫——这就是多态的雏形:同样的方法名,不同对象有不同的行为。
5.2 重写后保留父类行为
重写不意味着"丢弃"父类的代码。用 super() 可以在重写的同时保留父类的逻辑:
class Animal:
def speak(self):
print("动物发出声音")
class Dog(Animal):
def speak(self):
super().speak() # 先调用父类版本
print("汪汪汪!") # 再添加自己的内容
dog = Dog()
dog.speak()
# 动物发出声音
# 汪汪汪!
记住两个选择:完全重写(不调
super,只写自己的逻辑)或扩展重写(先super调父类,再添加自己的逻辑)。根据需求选。
六、多继承基础
6.1 多继承语法
Python 支持一个子类继承多个父类,语法是在括号里写多个父类,用逗号分隔:
class Flyable:
def fly(self):
print("正在飞行")
class Swimmable:
def swim(self):
print("正在游泳")
class Duck(Flyable, Swimmable):
pass
duck = Duck()
duck.fly() # 正在飞行 ← 来自 Flyable
duck.swim() # 正在游泳 ← 来自 Swimmable
Duck 同时继承了 Flyable 和 Swimmable,既有 fly 方法又有 swim 方法。
6.2 同名方法的查找顺序
如果多个父类有同名方法,Python 按从左到右的顺序查找:
class A:
def hello(self):
print("A 的 hello")
class B:
def hello(self):
print("B 的 hello")
class C(A, B):
pass
c = C()
c.hello() # A 的 hello ← A 写在前面,优先找到
多继承时父类顺序很重要:
class C(A, B)和class C(B, A)结果可能不同。具体查找顺序由 MRO 决定,下一节讲。
七、MRO 概念简述
MRO(Method Resolution Order,方法解析顺序)决定了 Python 在继承链中按什么顺序查找方法。Python 使用 C3 线性化算法,保证每个类只被访问一次。
用 类名.__mro__ 或 类名.mro() 可以查看查找顺序:
class A:
def hello(self):
print("A")
class B(A):
pass
class C(A):
def hello(self):
print("C")
class D(B, C):
pass
d = D()
d.hello() # C ← MRO: D → B → C → A
print(D.__mro__)
# (<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)
查找过程:D 没有 hello → 找 B(没有)→ 找 C(有!执行 C 的 hello)→ 不再继续找 A。
这就是经典的钻石继承(菱形继承):
A
/ \
B C
\ /
D
B 和 C 都继承 A,D 同时继承 B 和 C,形状像钻石。Python 的 MRO 保证 A 只在最后被访问一次,不会重复执行。
新手阶段了解 MRO 的概念即可,知道用
__mro__查看顺序就行。实际开发中遇到多继承的复杂问题再深入。
八、Scratch 积木对照
| Scratch | Python | 说明 |
|---|---|---|
| 克隆体继承母体属性 | class Dog(Animal): |
Scratch 克隆体自带母体属性,Python 子类自带父类属性和方法 |

# Scratch「克隆体继承母体属性」→ Python 对照
# Scratch: 克隆出来的角色自带母体的变量值
# Python: 子类自动拥有父类的属性和方法
class Animal:
def __init__(self, name):
self.name = name
class Dog(Animal): # Dog 继承 Animal,自带 name 属性
pass
dog = Dog("旺财")
print(dog.name) # 旺财 ← 继承来的
Scratch 中克隆体出生时就带着母体的变量值,不需要重新设置。Python 的继承也是类似的——子类创建对象时,父类的属性和方法自动就位,不用重新写。
九、综合实战:动物王国继承体系
综合运用类定义、继承、super()、方法重写、多态基础,搭建一个完整的动物继承体系。
# ========================================
# 动物王国继承体系
# ========================================
# 基类:动物
class Animal:
def __init__(self, name, age):
self.name = name
self.age = age
def speak(self):
print(f"{self.name}发出声音")
def info(self):
print(f"我是{self.name},今年{self.age}岁")
# 子类:狗(重写 speak + 重写 info)
class Dog(Animal):
def __init__(self, name, age, breed):
super().__init__(name, age) # 调用父类初始化 name 和 age
self.breed = breed # 子类新增属性
def speak(self):
print(f"{self.name}说:汪汪汪!") # 重写 speak
def info(self):
super().info() # 先调用父类 info
print(f"品种:{self.breed}") # 再补充自己的信息
# 子类:猫(只重写 speak)
class Cat(Animal):
def speak(self):
print(f"{self.name}说:喵喵喵!") # 重写 speak
# 子类:鸟(重写 speak + 新增 fly)
class Bird(Animal):
def __init__(self, name, age, wing_length):
super().__init__(name, age)
self.wing_length = wing_length
def speak(self):
print(f"{self.name}说:叽叽喳喳!")
def fly(self):
print(f"{self.name}展翅飞翔,翼展{self.wing_length}厘米")
# ========== 展示继承与多态 ==========
print("=" * 40)
print(" 动物王国继承体系演示")
print("=" * 40)
# 创建不同动物,放进同一个列表
animals = [
Dog("旺财", 3, "金毛"),
Cat("咪咪", 2),
Bird("小蓝", 1, 15),
]
# 遍历所有动物,统一调用 speak —— 多态的体现
for animal in animals:
animal.info()
animal.speak()
# 鸟可以飞,其他动物没有 fly 方法
if isinstance(animal, Bird):
animal.fly()
print("-" * 30)
运行结果:
========================================
动物王国继承体系演示
========================================
我是旺财,今年3岁
品种:金毛
旺财说:汪汪汪!
------------------------------
我是咪咪,今年2岁
咪咪说:喵喵喵!
------------------------------
我是小蓝,今年1岁
小蓝说:叽叽喳喳!
小蓝展翅飞翔,翼展15厘米
------------------------------
注意:旺财是狗,不会飞,所以没有调用
fly()。isinstance(animal, Bird)判断旺财不是Bird,跳过了fly调用。只有小蓝是Bird,才执行了fly()。这就是isinstance做类型判断的用处。
分步拆解:
Animal基类:定义了name、age两个属性,speak()和info()两个方法。所有子类都能继承这些Dog子类:通过super().__init__(name, age)初始化父类属性,新增breed属性。重写了speak()(输出汪汪)和info()(先调super保留父类信息,再补充品种)Cat子类:不需要新增属性,所以不写__init__,自动用父类的。只重写了speak()(输出喵喵)- Bird 子类:新增
wing_length属性,重写speak(),还新增了fly()方法——这是父类没有的 - 多态演示:把
Dog、Cat、Bird放进同一个列表,循环调用animal.speak()。同样的方法调用,不同对象表现出不同行为——这就是多态。用isinstance(animal, Bird)判断是否是鸟,是鸟才调fly()
十、常见报错速查表
| 报错信息 | 出现场景 | 原因 | 解决方法 |
|---|---|---|---|
AttributeError: 'Dog' object has no attribute 'name' |
子类访问父类属性 | 子类 __init__ 没调用 super().__init__(),父类属性未初始化 |
在子类 __init__ 中添加 super().__init__(name, age) |
TypeError: __init__() missing 2 required positional arguments |
创建对象时 | 传参数量与 __init__ 定义不匹配 |
检查 __init__ 参数列表,传入所有必需参数 |
TypeError: Cannot create a consistent method resolution order (MRO) |
多继承时 | 继承顺序矛盾,如 class C(A, B) 但 B 继承了 A |
调整继承顺序,确保父类顺序合理 |
AttributeError: 'super' object has no attribute 'fly' |
super() 调用方法时 |
父类中没有定义该方法 | 确认父类确实有该方法,检查方法名拼写 |
TypeError: speak() takes 0 positional arguments but 1 was given |
调用实例方法时 | 方法定义忘记写 self 参数 |
实例方法第一个参数必须是 self |
NameError: name 'Animal' is not defined |
定义子类时 | 父类在子类之后定义,或父类名拼写错误 | 确保父类在子类之前定义,检查类名拼写 |
十二、本章小结
本章你掌握了 Python 继承的完整用法:
- 单继承——
class 子类(父类):,子类自动拥有父类的属性和方法,还能扩展自己的 super()——super().__init__()调用父类初始化,super().方法名()调用父类普通方法- 方法重写——子类定义与父类同名的方法,覆盖父类行为;可用
super()保留父类逻辑 - 多继承——
class C(A, B):,一个子类继承多个父类,按 MRO 顺序查找方法 - MRO——方法解析顺序,用
类名.__mro__查看,保证每个类只访问一次
继承让代码复用变得简单,重写让定制行为变得灵活,多态让同一接口应对不同对象。
往期回顾:
零基础学 Python 第1章 | 全平台装环境 + VS Code 配置 + 第一个 Hello World
零基础学 Python 第2章 | print 输出 + 变量赋值 + input 交互输入
零基础学 Python 第3章 | 条件判断:if / elif / else
零基础学 Python 第 4 章 | for 循环与 range():让程序帮你重复干活
零基础学 Python 第 5 章 | while 循环与循环嵌套:让程序看着条件自己干活
零基础学 Python 第 6 章 | 元组与集合:不可变数据与自动去重
零基础学 Python 第7章 | 字典 dict:键值对存储
零基础学 Python 第9章 | 函数基础:def、参数与返回值
零基础学 Python 第10章 | 函数进阶:参数灵活用法与作用域
零基础学 Python 第12章 | 文件读写:让数据持久保存
零基础学 Python 第13章 | 异常处理:try-except 让程序不崩溃
零基础学 Python 第15章 | 类与对象:面向对象编程入门
下一章预告:到目前为止,类的属性都是公开的,谁都能改。下一章学习封装与 property——用私有属性保护数据,用 property 控制属性的读写,让代码更安全、更专业。
文末资源区
| 资源 | 说明 |
|---|---|
| Python 官方文档 - 继承 | https://docs.python.org/3/tutorial/classes.html#inheritance |
Python 官方文档 - super() |
https://docs.python.org/3/library/functions.html#super |
| Python 官方文档 - 多继承与 MRO | https://docs.python.org/3/howto/mro.html |
| 本章完整源码 | chapter16_all.py(关注梅雅达编程笔记,评论领资料) |
每节课都配套课后练习题与完整源码。代码报错、看不懂知识点,可在评论区粘贴问题,逐一解答!
欢迎关注梅雅达编程笔记,持续分享Python零基础学习内容
更多推荐





所有评论(0)