第6章 6.2 继承、抽象类、接口
——上一章我们学会了把数据和函数打包进类,这一章要让类学会「遗传」和「分工」
🎯 开场 3 分钟:为什么要学这个?
你有没有遇到过这种情况——
写了 100 行代码实现「猫」的功能,后来发现还要写「狗」,然后又要写「鸟」……每个都要重写吃饭、睡觉、叫唤这些通用方法,代码越写越多,人越写越累。
或者——
写了一个「读取配置文件」的函数,队友非要你改成读取数据库,你的整个代码结构要推翻重来。
这就是继承和接口要解决的问题。
学完这一章,你只需要:
1. 写一次「动物」的基础能力
2. 让「猫」「狗」自动继承这些能力
3. 规定好「所有动物必须会什么」,新动物往里套就行
🧱 基础 25 分钟:核心概念
1. 继承——儿子直接用老子的东西
生活类比: 你爸妈是医生,你从小耳濡目染医学知识,不用重新学就能继承他们的「医学底子」。这就是「继承」。
为什么要用: 减少重复代码。「猫会吃饭、狗会吃饭、鸟也会吃饭」——与其每个\n\n
\n\n
\n\n类都写一遍,不如写一个「动物」类,让它们都继承。
怎么用:
# 父类(基类)
class Animal:
def __init__(self, name):
self.name = name
def eat(self):
print(f"{self.name} 正在吃饭")
# 子类(派生类)
class Cat(Animal):
def meow(self):
print(f"{self.name} 喵喵叫")
# 使用
cat = Cat("小橘")
cat.eat() # 继承自父类,直接能用
cat.meow() # 子类自己的方法
输出:
小橘 正在吃饭
小橘 喵喵叫
子类 Cat 什么都没写,却天生会「吃饭」——这就是继承的力量。
2. 重写——儿子也可以有自己的想法
生活类比: 你爸是中医,你学医后觉得西医也不错,于是你同时会中医和西医——你「重写」了你爸的治病方法。
为什么要用: 继承来的方法不一定完全适用,需要改造。
怎么用:
class Animal:
def sound(self):
print("发出声音")
class Cat(Animal):
def sound(self): # 重写父类的 sound 方法
print("喵喵叫")
class Dog(Animal):
def sound(self): # 重写父类的 sound 方法
print("汪汪叫")
cat = Cat("小橘")
dog = Dog("大黄")
cat.sound() # 输出:喵喵叫
dog.sound() # 输出:汪汪叫
输出:
喵喵叫
汪汪叫
同样的 sound() 方法,不同的子类给出不同的结果——这叫「多态」。
3. super()——调用父类的力量
生活类比: 你继承了你爸的医术,但你爸还有一招祖传膏药,你得用 super() 才能调用到。
为什么要用: 重写方法时,还想保留父类的功能,只是扩展一下。
怎么用:
class Animal:
def __init__(self, name):
self.name = name
self.health = 100
class Cat(Animal):
def __init__(self, name, color):
super().__init__(name) # 先调用父类的 __init__
self.color = color # 再加自己的属性
cat = Cat("小橘", "橙色")
print(f"名字:{cat.name}, 颜色:{cat.color}, 生命值:{cat.health}")
输出:
名字:小橘, 颜色:橙色, 生命值:100
4. 抽象类——我只管定规矩,不管实现
生活类比: 劳动合同只规定「每天工作 8 小时」「每月发工资」,但不规定你具体怎么工作——这叫「抽象」。抽象类就是只定规矩,不做具体事。
为什么要用: 强制所有子类必须实现某些方法,保证大家都有统一的行为。
怎么用:
from abc import ABC, abstractmethod
class Shape(ABC): # 必须继承 ABC 才能成为抽象类
@abstractmethod
def area(self): # 抽象方法——子类必须实现
pass
@abstractmethod
def perimeter(self):
pass
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self): # 必须实现父类的抽象方法
return self.width * self.height
def perimeter(self):
return 2 * (self.width + self.height)
rect = Rectangle(5, 3)
print(f"面积:{rect.area()}, 周长:{rect.perimeter()}")
输出:
面积:15, 周长:16
注意! 如果 Rectangle 没实现 area() 或 perimeter(),代码会直接报错——抽象类就是这么严格。
5. 接口——约定好「会什么」,不关心「怎么会的」
生活类比: USB 接口只规定「这个口子能插进去」「能传输数据」,不规定你插的是U盘还是鼠标——这就是「接口」的意义。
Python 的接口实现: Python 没有 interface 关键字,但可以用抽象类模拟。
怎么用:
from abc import ABC, abstractmethod
# 第一个接口:会飞的
class Flyable(ABC):
@abstractmethod
def fly(self):
pass
# 第二个接口:会叫的
class Speakable(ABC):
@abstractmethod
def speak(self):
pass
# 鹦鹉:既会飞又会叫
class Parrot(Flyable, Speakable):
def fly(self):
print("鹦鹉扇翅膀飞")
def speak(self):
print("鹦鹉学人说话")
parrot = Parrot()
parrot.fly()
parrot.speak()
输出:
鹦鹉扇翅膀飞
鹦鹉学人说话
一个类可以实现多个接口——就像你同时是「员工」又是「球迷」。
🔥 实战 35 分钟:3 个递进的小项目
项目 1(5 分钟):动物叫声模拟器
跟着抄就能跑,理解核心 API。
from abc import ABC, abstractmethod
class Animal(ABC):
def __init__(self, name):
self.name = name
@abstractmethod
def make_sound(self):
pass
class Dog(Animal):
def make_sound(self):
return f"{self.name} 说:汪汪汪"
class Cat(Animal):
def make_sound(self):
return f"{self.name} 说:喵喵喵"
class Cow(Animal):
def make_sound(self):
return f"{self.name} 说:哞哞哞"
# 测试
animals = [Dog("大黄"), Cat("小橘"), Cow("花花")]
for animal in animals:
print(animal.make_sound())
预期输出:
大黄 说:汪汪汪
小橘 说:喵喵喵
花花 说:哞哞哞
一行话解释: 定义抽象类 Animal 要求所有动物实现 make_sound(),然后让不同动物继承它。
项目 2(15 分钟):员工管理系统
从字典读取数据,按「岗位」分类处理。
from abc import ABC, abstractmethod
class Employee(ABC):
def __init__(self, name, employee_id, salary):
self.name = name
self.employee_id = employee_id
self.salary = salary
@abstractmethod
def get_role(self):
pass
def display_info(self):
print(f"工号:{self.employee_id}, 姓名:{self.name}, 岗位:{self.get_role()}, 薪资:{self.salary}")
class Developer(Employee):
def get_role(self):
return "开发工程师"
class Designer(Employee):
def get_role(self):
return "设计师"
class Manager(Employee):
def get_role(self):
return "经理"
# 模拟从 CSV 读取的数据
employee_data = [
{"name": "张三", "employee_id": "E001", "salary": 15000, "type": "Developer"},
{"name": "李四", "employee_id": "E002", "salary": 12000, "type": "Designer"},
{"name": "王五", "employee_id": "E003", "salary": 20000, "type": "Manager"},
{"name": "赵六", "employee_id": "E004", "salary": 14000, "type": "Developer"},
]
# 根据类型创建员工对象
def create_employee(data):
employee_type = data["type"]
if employee_type == "Developer":
return Developer(data["name"], data["employee_id"], data["salary"])
elif employee_type == "Designer":
return Designer(data["name"], data["employee_id"], data["salary"])
elif employee_type == "Manager":
return Manager(data["name"], data["employee_id"], data["salary"])
# 批量创建并显示
employees = [create_employee(d) for d in employee_data]
print("=== 员工信息 ===")
for emp in employees:
emp.display_info()
预期输出:
=== 员工信息 ===
工号:E001, 姓名:张三, 岗位:开发工程师, 薪资:15000
工号:E002, 姓名:李四, 岗位:设计师, 薪资:12000
工号:E003, 姓名:王五, 岗位:经理, 薪资:20000
工号:E004, 姓名:赵六, 岗位:开发工程师, 薪资:14000
一行话解释: 用抽象类定义「员工」的标准,所有岗位都继承它,代码扩展新岗位只需要加一个类。
项目 3(15 分钟):简单任务管理系统
组合继承和接口,做一个带分类的任务管理工具。
from abc import ABC, abstractmethod
# 接口:可完成的
class Completable(ABC):
@abstractmethod
def complete(self):
pass
# 接口:可优先级的
class Prioritizable(ABC):
@abstractmethod
def get_priority(self):
pass
# 任务基类
class Task:
def __init__(self, title, description):
self.title = title
self.description = description
self.is_done = False
def __str__(self):
status = "✓" if self.is_done else "✗"
return f"[{status}] {self.title}"
# 普通任务:可完成
class SimpleTask(Task, Completable):
def __init__(self, title, description):
super().__init__(title, description)
def complete(self):
self.is_done = True
print(f"任务「{self.title}」已完成")
# 优先级任务:可完成 + 可设置优先级
class PriorityTask(Task, Completable, Prioritizable):
def __init__(self, title, description, priority):
super().__init__(title, description)
self.priority = priority # 1=高, 2=中, 3=低
def get_priority(self):
return self.priority
# 任务管理器
class TaskManager:
def __init__(self):
self.tasks = []
def add_task(self, task):
self.tasks.append(task)
print(f"已添加任务:{task.title}")
def show_all(self):
print("\n=== 任务列表 ===")
for task in self.tasks:
if isinstance(task, PriorityTask):
print(f"{task} (优先级:{task.get_priority()})")
else:
print(task)
def show_pending(self):
print("\n=== 待完成任务 ===")
for task in self.tasks:
if not task.is_done:
print(task)
# 使用
manager = TaskManager()
# 添加几个任务
task1 = SimpleTask("写日报", "今天的工作总结")
task2 = PriorityTask("修复Bug", "登录页面崩溃了", priority=1)
task3 = PriorityTask("整理文档", "更新接口文档", priority=3)
manager.add_task(task1)
manager.add_task(task2)
manager.add_task(task3)
# 显示所有任务
manager.show_all()
# 完成任务
task2.complete()
# 显示待完成
manager.show_pending()
预期输出:
已添加任务:写日报
已添加任务:修复Bug
已添加任务:整理文档
=== 任务列表 ===
[✗] 写日报
[✗] 修复Bug (优先级:1)
[✗] 整理文档 (优先级:3)
=== 待完成任务 ===
[✗] 写日报
[✗] 整理文档 (优先级:3)
一行话解释: 用接口分离「可完成」和「可优先级」两个功能,类想用哪个功能就实现哪个接口。
💪 进阶 20 分钟:常见坑 + 性能小贴士
坑 1:忘记调用 super().init()
# ❌ 错误示例
class Child(Parent):
def __init__(self, name, child_data):
self.name = name # 覆盖了父类的 name
self.child_data = child_data # 父类的属性没初始化
# ✅ 正确示例
class Child(Parent):
def __init__(self, name, child_data):
super().__init__(name) # 先调用父类初始化
self.child_data = child_data
注意! 不调用 super().init(),父类的属性可能压根没创建,后续用的时候会报错。
坑 2:抽象类可以直接实例化
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
# ❌ 错误示例
s = Shape() # 报错!抽象类不能直接实例化
# ✅ 正确示例
class Rectangle(Shape):
def area(self):
return 10
r = Rectangle() # 正确,必须先实现抽象方法
坑 3:多重继承时的 MRO(方法解析顺序)
class A:
def test(self):
print("A")
class B(A):
def test(self):
print("B")
class C(A):
def test(self):
print("C")
class D(B, C):
pass
# ❌ 错误示例:以为会调用 C 的 test
d = D()
d.test() # 输出:B(不是 C!)
# ✅ 正确示例:理解 MRO 顺序
print(D.__mro__) # 可以查看类的继承顺序
注意! Python 用「从左到右、广度优先」的顺序解析方法,不一定是你以为的顺序。
坑 4:接口只定义不实现
from abc import ABC, abstractmethod
class MyInterface(ABC):
@abstractmethod
def my_method(self):
pass # 这里写什么都没用,子类必须自己实现
# ❌ 错误示例:子类也没实现
class MyClass(MyInterface):
pass # 报错!MyClass 还是抽象类
# ✅ 正确示例
class MyClass(MyInterface):
def my_method(self): # 必须实现
print("实现了!")
坑 5:把所有东西都写成继承
# ❌ 错误示例:过度使用继承
class Animal:
pass
class Cat(Animal): # 猫是动物,合理
class Dog(Animal): # 狗是动物,合理
class Car(Cat): # ??? 车是猫?荒谬!
# ✅ 正确示例:优先用「组合」而不是「继承」
class Engine:
pass
class Car:
def __init__(self):
self.engine = Engine() # 车有一个发动机,不是「是」一个发动机
调试技巧:查看类的继承关系
# 查看一个类继承的所有父类
print(MyClass.__mro__) # 返回元组
# 查看对象的所有可用方法
print(dir(my_object))
# isinstance 和 issubclass 的区别
print(isinstance(obj, MyClass)) # obj 是 MyClass 的实例吗?
print(issubclass(MyClass, Parent)) # MyClass 是 Parent 的子类吗?
✏️ 练习题
练习 1(2 分钟):抄改「动物叫声」
# 输入:把 Cat 类改成 Bird 类,鸟叫是「叽叽喳喳」
# 预期输出:
# 小鸟 说:叽叽喳喳
# 提示:复制 Cat 类代码,改类名和叫声就行
练习 2(2 分钟):加一个判断
# 在项目 1 基础上,如果动物是「狗」,额外打印「这是人类的好朋友」
# 预期输出:
# 大黄 说:汪汪汪
# 这是人类的好朋友
# 小橘 说:喵喵喵
# 提示:在 Dog 的 make_sound 方法里加 print,或者在调用时用 isinstance 判断
练习 3(3 分钟):新增「设计师」岗位
# 在项目 2 的员工系统里,新增一个 Tester(测试工程师)岗位
# type 填 "Tester",薪资自己定
# 预期输出:
# 工号:E005, 姓名:测试员, 岗位:测试工程师, 薪资:13000
# 提示:仿照 Developer 或 Designer 写一个 Tester 类,然后在 employee_data 里加一条
练习 4(5 分钟):合并两个项目
# 把项目 2(员工管理)和项目 3(任务管理)合并
# 要求:给每个 Employee 增加一个「接受任务」的方法
# 预期输出:员工对象可以调用 .accept_task(task),把任务分配给它
# 提示:在 Employee 类里加一个 tasks 列表和 accept_task 方法
练习 5(5 分钟):分析报错
# 以下代码会报错,请分析原因并修复
from abc import ABC, abstractmethod
class Base(ABC):
@abstractmethod
def method(self):
return "Base"
class Child(Base):
pass # 这里缺少了什么?
obj = Child()
print(obj.method())
# 预期报错:TypeError: Can't instantiate abstract class Child with abstract method method
# 提示:Child 类虽然继承了 Base,但没实现 method(),所以还是抽象类
作业:做一个「宠物店管理系统」
需求描述: 用继承和接口做一个宠物店管理系统,能管理不同类型的宠物。
功能点:
1. 定义抽象类 Pet,要求实现 get_sound() 和 get_price() 方法
2. 创建至少 3 种宠物(猫、狗、仓鼠)
3. 实现接口 Sellable,让宠物可以被「出售」,出售时打印交易信息
加分项:
1. 从 JSON 文件读取宠物数据(模拟数据自己准备)
2. 实现「折扣」功能,某些宠物可以打折
验收标准:
- 能跑起来
- 输出符合预期(有价格、有叫声)
- 代码有注释(每类 1-2 句说明)
提交方式: 评论区贴代码或 GitHub 链接
📚 总结 + 资源
本文学到的 3 个核心点:
1. 继承——子类自动拥有父类的方法,减少重复代码
2. 抽象类——定规矩,强制子类实现某些方法
3. 接口——约定「会什么」,用抽象类模拟,不关心具体实现
延伸学习资源:
- 官方文档:Python 面向对象编程(英文,但例子清晰)
- 书籍:《Python 编程:从入门到实践》第 9 章「类和实例」
- 视频:B 站「Python 面向对象编程」系列(搜「莫烦 Python」)
互动钩子: 你在工作中有没有遇到过「这个功能好像能用继承解决,但用不好」的情况?评论区聊聊,老粉优先回复!
下章预告: 下一章我们要聊一个「很玄」的东西——Python 里有那些双下划线开头结尾的方法,比如
__init__、__str__、__len__……它们是怎么被触发的?「==」为什么能比较两个对象?第 6 章 6.3 魔术方法与魔术常量,不见不散。

评论(0)