Python实战第12期:类的继承与多态

0 阅读12分钟

在这里插入图片描述

引言:为什么需要继承?

在前面的课程中,我们学习了如何定义类。但如果多个类有相同的属性和方法,我们需要重复编写相同的代码。

比如:

  • 动物类有名字、年龄,有吃、睡的方法
  • 狗类继承自动物类,有名字、年龄,有吃、睡、叫的方法
  • 猫类继承自动物类,有名字、年龄,有吃、睡、抓老鼠的方法

如果没有继承,我们需要在狗类和猫类中重复定义名字、年龄、吃、睡。有了继承,我们可以让狗类和猫类继承自动物类,自动获得这些属性和方法。

继承是面向对象编程的重要特性,它允许我们基于已有的类创建新类,实现代码复用。


一、继承的概念

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顺序)

九、本篇小结

今天,我们完成了以下学习内容:

  1. 继承的概念:

    • 子类继承父类的属性和方法
    • 使用 class 子类(父类) 语法
    • 继承实现代码复用
  2. 方法重写:

    • 子类重新定义父类的方法
    • 使用 super() 调用父类方法
    • 扩展父类方法的功能
  3. 多态的应用:

    • 同一个方法在不同对象上有不同行为
    • 提高代码的灵活性和可扩展性
    • 工厂模式、策略模式等设计模式
  4. 高级特性:

    • 多重继承和MRO
    • Mixin模式
    • 抽象基类

请记住这三个结论:

  1. 继承实现代码复用:子类自动拥有父类的属性和方法
  2. 方法重写扩展功能:使用super()调用父类方法后再添加新功能
  3. 多态提高灵活性:同一个接口,不同的实现

下期预告

在第13期中,我们将学习Python的模块与包,包括:

  • 导入模块的方法
  • 创建自己的模块
  • 使用pip安装第三方库
  • 包的组织和发布

敬请期待!


✍坚持原创,求关注,点赞,收藏