引言:为什么需要继承?
在前面的课程中,我们学习了如何定义类。但如果多个类有相同的属性和方法,我们需要重复编写相同的代码。
比如:
- 动物类有名字、年龄,有吃、睡的方法
- 狗类继承自动物类,有名字、年龄,有吃、睡、叫的方法
- 猫类继承自动物类,有名字、年龄,有吃、睡、抓老鼠的方法
如果没有继承,我们需要在狗类和猫类中重复定义名字、年龄、吃、睡。有了继承,我们可以让狗类和猫类继承自动物类,自动获得这些属性和方法。
继承是面向对象编程的重要特性,它允许我们基于已有的类创建新类,实现代码复用。
一、继承的概念
1. 什么是继承
继承是指一个类(子类)可以继承另一个类(父类)的属性和方法。
- 父类(基类):被继承的类
- 子类(派生类):继承其他类的类
# 父类
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):
def bark(self):
print(f"{self.name}:汪汪汪!")
# 创建子类对象
dog = Dog("旺财", 3)
# 子类对象可以使用父类的属性和方法
print(f"名字:{dog.name},年龄:{dog.age}")
dog.eat() # 输出:旺财在吃东西
dog.sleep() # 输出:旺财在睡觉
dog.bark() # 输出:旺财:汪汪汪!
💡 提示:
- 子类自动拥有父类的所有属性和方法
- 子类可以添加自己特有的属性和方法
2. 继承的语法
# 基本语法
class 子类(父类):
# 子类特有的属性和方法
pass
# 示例
class Parent:
def __init__(self, name):
self.name = name
def show(self):
print(f"父类:{self.name}")
class Child(Parent):
def __init__(self, name, age):
super().__init__(name) # 调用父类的__init__
self.age = age
def show(self):
super().show() # 调用父类的show方法
print(f"子类:{self.age}岁")
# 测试
child = Child("小明", 20)
child.show()
输出:
父类:小明
子类:20岁
3. 继承的好处
# 没有继承:重复代码
class Dog:
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}在睡觉")
def bark(self):
print(f"{self.name}:汪汪汪!")
class Cat:
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}在睡觉")
def meow(self):
print(f"{self.name}:喵喵喵!")
# 使用继承:代码复用
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):
def bark(self):
print(f"{self.name}:汪汪汪!")
class Cat(Animal):
def meow(self):
print(f"{self.name}:喵喵喵!")
💡 提示:继承可以减少代码重复,提高代码的可维护性。
二、方法重写
1. 什么是方法重写
方法重写是指子类重新定义父类中已有的方法,以改变或扩展其行为。
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
print(f"{self.name}发出声音")
class Dog(Animal):
def speak(self): # 重写父类方法
print(f"{self.name}:汪汪汪!")
class Cat(Animal):
def speak(self): # 重写父类方法
print(f"{self.name}:喵喵喵!")
# 测试
dog = Dog("旺财")
cat = Cat("小花")
dog.speak() # 输出:旺财:汪汪汪!
cat.speak() # 输出:小花:喵喵喵!
2. 使用super()调用父类方法
class Animal:
def __init__(self, name, age):
self.name = name
self.age = age
def info(self):
print(f"名字:{self.name},年龄:{self.age}")
class Dog(Animal):
def __init__(self, name, age, breed):
super().__init__(name, age) # 调用父类的__init__
self.breed = breed
def info(self):
super().info() # 调用父类的info方法
print(f"品种:{self.breed}")
# 测试
dog = Dog("旺财", 3, "金毛")
dog.info()
输出:
名字:旺财,年龄:3
品种:金毛
💡 提示:
super()返回父类的引用- 使用
super()可以调用父类的方法
3. 扩展父类方法
class Shape:
def __init__(self, color):
self.color = color
def draw(self):
print(f"绘制{self.color}色的图形")
class Circle(Shape):
def __init__(self, color, radius):
super().__init__(color)
self.radius = radius
def draw(self):
super().draw() # 先调用父类方法
print(f"绘制半径为{self.radius}的圆形")
class Rectangle(Shape):
def __init__(self, color, width, height):
super().__init__(color)
self.width = width
self.height = height
def draw(self):
super().draw() # 先调用父类方法
print(f"绘制宽{self.width}高{self.height}的矩形")
# 测试
circle = Circle("红色", 5)
circle.draw()
rectangle = Rectangle("蓝色", 4, 6)
rectangle.draw()
输出:
绘制红色的图形
绘制半径为5的圆形
绘制蓝色的图形
绘制宽4高6的矩形
三、多态的应用
1. 什么是多态
多态是指同一个方法在不同的对象上有不同的行为。
class Animal:
def speak(self):
pass
class Dog(Animal):
def speak(self):
return "汪汪汪"
class Cat(Animal):
def speak(self):
return "喵喵喵"
class Duck(Animal):
def speak(self):
return "嘎嘎嘎"
# 多态:同一个方法,不同的行为
animals = [Dog(), Cat(), Duck()]
for animal in animals:
print(animal.speak())
输出:
汪汪汪
喵喵喵
嘎嘎嘎
💡 提示:
- 多态让代码更灵活
- 不需要知道对象的具体类型,只需要知道它有某个方法
2. 多态的实际应用
class Payment:
"""支付基类"""
def pay(self, amount):
pass
class Alipay(Payment):
"""支付宝支付"""
def pay(self, amount):
print(f"使用支付宝支付{amount}元")
class WechatPay(Payment):
"""微信支付"""
def pay(self, amount):
print(f"使用微信支付{amount}元")
class CreditCard(Payment):
"""信用卡支付"""
def pay(self, amount):
print(f"使用信用卡支付{amount}元")
# 多态应用
def process_payment(payment_method, amount):
"""处理支付"""
payment_method.pay(amount)
# 测试
alipay = Alipay()
wechat = WechatPay()
credit = CreditCard()
process_payment(alipay, 100) # 输出:使用支付宝支付100元
process_payment(wechat, 200) # 输出:使用微信支付200元
process_payment(credit, 300) # 输出:使用信用卡支付300元
3. 多态与工厂模式
class Document:
"""文档基类"""
def __init__(self, content):
self.content = content
def show(self):
pass
class PDFDocument(Document):
"""PDF文档"""
def show(self):
print(f"[PDF] {self.content}")
class WordDocument(Document):
"""Word文档"""
def show(self):
print(f"[Word] {self.content}")
class ExcelDocument(Document):
"""Excel文档"""
def show(self):
print(f"[Excel] {self.content}")
# 工厂函数
def create_document(doc_type, content):
"""根据类型创建文档"""
if doc_type == "pdf":
return PDFDocument(content)
elif doc_type == "word":
return WordDocument(content)
elif doc_type == "excel":
return ExcelDocument(content)
else:
raise ValueError(f"不支持的文档类型:{doc_type}")
# 测试
documents = [
create_document("pdf", "这是一份PDF文档"),
create_document("word", "这是一份Word文档"),
create_document("excel", "这是一份Excel文档")
]
for doc in documents:
doc.show()
输出:
[PDF] 这是一份PDF文档
[Word] 这是一份Word文档
[Excel] 这是一份Excel文档
四、多重继承
1. 什么是多重继承
Python支持一个类继承多个父类。
class Father:
def __init__(self, name):
self.name = name
def show_father(self):
print(f"父亲:{self.name}")
class Mother:
def __init__(self, age):
self.age = age
def show_mother(self):
print(f"母亲年龄:{self.age}")
class Child(Father, Mother):
def __init__(self, name, age, school):
Father.__init__(self, name)
Mother.__init__(self, age)
self.school = school
def show_child(self):
print(f"学校:{self.school}")
# 测试
child = Child("小明", 35, "实验小学")
child.show_father() # 输出:父亲:小明
child.show_mother() # 输出:母亲年龄:35
child.show_child() # 输出:学校:实验小学
💡 提示:多重继承虽然强大,但可能导致复杂的继承关系,建议谨慎使用。
2. MRO(方法解析顺序)
class A:
def show(self):
print("A")
class B(A):
def show(self):
print("B")
class C(A):
def show(self):
print("C")
class D(B, C):
pass
# 查看MRO
print(D.__mro__)
# 测试
d = D()
d.show() # 输出:B(按照MRO顺序,先找到B)
💡 提示:Python使用C3线性化算法确定MRO,保证继承关系的一致性。
3. Mixin模式
class LogMixin:
"""日志混入类"""
def log(self, message):
print(f"[LOG] {message}")
class CacheMixin:
"""缓存混入类"""
def __init__(self):
self._cache = {}
def get_cache(self, key):
return self._cache.get(key)
def set_cache(self, key, value):
self._cache[key] = value
class UserService(LogMixin, CacheMixin):
"""用户服务类"""
def __init__(self):
super().__init__()
def get_user(self, user_id):
# 先查缓存
user = self.get_cache(user_id)
if user:
self.log(f"从缓存获取用户{user_id}")
return user
# 从数据库获取
self.log(f"从数据库获取用户{user_id}")
user = {"id": user_id, "name": f"用户{user_id}"}
self.set_cache(user_id, user)
return user
# 测试
service = UserService()
user1 = service.get_user(1)
user2 = service.get_user(1) # 这次从缓存获取
输出:
[LOG] 从数据库获取用户1
[LOG] 从缓存获取用户1
五、抽象基类
1. 什么是抽象基类
抽象基类(ABC) 是不能被实例化的类,用于定义接口规范。
from abc import ABC, abstractmethod
class Shape(ABC):
"""形状抽象基类"""
@abstractmethod
def area(self):
"""计算面积(抽象方法)"""
pass
@abstractmethod
def perimeter(self):
"""计算周长(抽象方法)"""
pass
# 不能实例化抽象类
# shape = Shape() # TypeError
# 必须实现所有抽象方法才能实例化
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14159 * self.radius ** 2
def perimeter(self):
return 2 * 3.14159 * self.radius
# 测试
circle = Circle(5)
print(f"面积:{circle.area():.2f}")
print(f"周长:{circle.perimeter():.2f}")
2. 抽象基类的应用
from abc import ABC, abstractmethod
class DataProcessor(ABC):
"""数据处理抽象基类"""
@abstractmethod
def load_data(self):
"""加载数据"""
pass
@abstractmethod
def process_data(self):
"""处理数据"""
pass
@abstractmethod
def save_data(self):
"""保存数据"""
pass
def run(self):
"""运行处理流程"""
self.load_data()
self.process_data()
self.save_data()
class CSVProcessor(DataProcessor):
"""CSV处理器"""
def load_data(self):
print("加载CSV数据")
def process_data(self):
print("处理CSV数据")
def save_data(self):
print("保存CSV数据")
class JSONProcessor(DataProcessor):
"""JSON处理器"""
def load_data(self):
print("加载JSON数据")
def process_data(self):
print("处理JSON数据")
def save_data(self):
print("保存JSON数据")
# 测试
csv_processor = CSVProcessor()
csv_processor.run()
print()
json_processor = JSONProcessor()
json_processor.run()
输出:
加载CSV数据
处理CSV数据
保存CSV数据
加载JSON数据
处理JSON数据
保存JSON数据
六、isinstance和issubclass
1. isinstance()
isinstance() 用于判断对象是否是某个类或其子类的实例。
class Animal:
pass
class Dog(Animal):
pass
class Cat(Animal):
pass
dog = Dog()
cat = Cat()
# 判断类型
print(isinstance(dog, Dog)) # 输出:True
print(isinstance(dog, Animal)) # 输出:True(Dog是Animal的子类)
print(isinstance(dog, Cat)) # 输出:False
print(isinstance(cat, Cat)) # 输出:True
print(isinstance(cat, Animal)) # 输出:True
# 可以判断多个类型
print(isinstance(dog, (Dog, Cat))) # 输出:True
2. issubclass()
issubclass() 用于判断一个类是否是另一个类的子类。
class Animal:
pass
class Dog(Animal):
pass
class Cat(Animal):
pass
# 判断继承关系
print(issubclass(Dog, Animal)) # 输出:True
print(issubclass(Cat, Animal)) # 输出:True
print(issubclass(Dog, Cat)) # 输出:False
# 类是自身的子类
print(issubclass(Dog, Dog)) # 输出:True
七、实战练习
练习1:员工管理系统
class Employee:
"""员工基类"""
def __init__(self, name, employee_id):
self.name = name
self.employee_id = employee_id
def calculate_salary(self):
"""计算薪资(子类需要实现)"""
raise NotImplementedError("子类必须实现此方法")
def info(self):
"""显示信息"""
print(f"工号:{self.employee_id},姓名:{self.name}")
class Manager(Employee):
"""经理"""
def __init__(self, name, employee_id, base_salary, bonus):
super().__init__(name, employee_id)
self.base_salary = base_salary
self.bonus = bonus
def calculate_salary(self):
return self.base_salary + self.bonus
def info(self):
super().info()
print(f"职位:经理,薪资:{self.calculate_salary()}元")
class Developer(Employee):
"""程序员"""
def __init__(self, name, employee_id, base_salary, projects):
super().__init__(name, employee_id)
self.base_salary = base_salary
self.projects = projects
def calculate_salary(self):
return self.base_salary + len(self.projects) * 1000
def info(self):
super().info()
print(f"职位:程序员,薪资:{self.calculate_salary()}元,项目数:{len(self.projects)}")
# 测试
employees = [
Manager("张三", "M001", 15000, 5000),
Developer("李四", "D001", 12000, ["项目A", "项目B"]),
Developer("王五", "D002", 10000, ["项目C"])
]
for emp in employees:
emp.info()
print()
练习2:图形计算器
from abc import ABC, abstractmethod
import math
class Shape(ABC):
"""图形抽象基类"""
@abstractmethod
def area(self):
pass
@abstractmethod
def perimeter(self):
pass
def __str__(self):
return f"面积:{self.area():.2f},周长:{self.perimeter():.2f}"
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return math.pi * self.radius ** 2
def perimeter(self):
return 2 * math.pi * self.radius
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)
class Triangle(Shape):
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
def area(self):
s = (self.a + self.b + self.c) / 2
return math.sqrt(s * (s - self.a) * (s - self.b) * (s - self.c))
def perimeter(self):
return self.a + self.b + self.c
# 测试
shapes = [
Circle(5),
Rectangle(4, 6),
Triangle(3, 4, 5)
]
for shape in shapes:
print(f"{shape.__class__.__name__}:{shape}")
练习3:策略模式
from abc import ABC, abstractmethod
class SortStrategy(ABC):
"""排序策略抽象基类"""
@abstractmethod
def sort(self, data):
pass
class BubbleSort(SortStrategy):
"""冒泡排序"""
def sort(self, data):
data = data.copy()
n = len(data)
for i in range(n):
for j in range(0, n - i - 1):
if data[j] > data[j + 1]:
data[j], data[j + 1] = data[j + 1], data[j]
print("使用冒泡排序")
return data
class QuickSort(SortStrategy):
"""快速排序"""
def sort(self, data):
if len(data) <= 1:
return data
pivot = data[len(data) // 2]
left = [x for x in data if x < pivot]
middle = [x for x in data if x == pivot]
right = [x for x in data if x > pivot]
print("使用快速排序")
return self.sort(left) + middle + self.sort(right)
class Sorter:
"""排序器"""
def __init__(self, strategy):
self.strategy = strategy
def set_strategy(self, strategy):
self.strategy = strategy
def sort(self, data):
return self.strategy.sort(data)
# 测试
data = [64, 34, 25, 12, 22, 11, 90]
sorter = Sorter(BubbleSort())
result1 = sorter.sort(data)
print(f"结果:{result1}\n")
sorter.set_strategy(QuickSort())
result2 = sorter.sort(data)
print(f"结果:{result2}")
八、常见问题与排错
问题1:忘记调用super().init()
原因:子类没有调用父类的构造方法 解决方法:使用super()调用父类构造方法
# ❌ 错误
class Parent:
def __init__(self, name):
self.name = name
class Child(Parent):
def __init__(self, name, age):
# 忘记调用super().__init__(name)
self.age = age
child = Child("小明", 20)
# print(child.name) # AttributeError
# ✅ 正确
class Child(Parent):
def __init__(self, name, age):
super().__init__(name) # 调用父类构造方法
self.age = age
child = Child("小明", 20)
print(child.name) # 输出:小明
问题2:方法重写时参数不一致
原因:子类方法的参数与父类不一致 解决方法:保持参数一致,或使用默认参数
# ❌ 不推荐
class Parent:
def process(self, data):
pass
class Child(Parent):
def process(self, data, extra): # 参数不一致
pass
# ✅ 推荐
class Child(Parent):
def process(self, data, extra=None): # 使用默认参数
pass
问题3:多重继承的菱形问题
原因:多重继承可能导致方法调用顺序混乱 解决方法:使用super()或避免复杂的多重继承
# 菱形继承
class A:
def show(self):
print("A")
class B(A):
def show(self):
super().show()
print("B")
class C(A):
def show(self):
super().show()
print("C")
class D(B, C):
def show(self):
super().show()
print("D")
# 测试
d = D()
d.show()
# 输出:A -> C -> B -> D(按照MRO顺序)
九、本篇小结
今天,我们完成了以下学习内容:
-
继承的概念:
- 子类继承父类的属性和方法
- 使用
class 子类(父类)语法 - 继承实现代码复用
-
方法重写:
- 子类重新定义父类的方法
- 使用
super()调用父类方法 - 扩展父类方法的功能
-
多态的应用:
- 同一个方法在不同对象上有不同行为
- 提高代码的灵活性和可扩展性
- 工厂模式、策略模式等设计模式
-
高级特性:
- 多重继承和MRO
- Mixin模式
- 抽象基类
请记住这三个结论:
- 继承实现代码复用:子类自动拥有父类的属性和方法
- 方法重写扩展功能:使用super()调用父类方法后再添加新功能
- 多态提高灵活性:同一个接口,不同的实现
下期预告
在第13期中,我们将学习Python的模块与包,包括:
- 导入模块的方法
- 创建自己的模块
- 使用pip安装第三方库
- 包的组织和发布
敬请期待!
✍坚持原创,求关注,点赞,收藏