第59集:多态特性

学习目标

  • 理解多态的概念和作用
  • 掌握方法重写实现多态
  • 学会使用抽象基类定义接口
  • 理解鸭子类型的概念
  • 掌握多态在设计中的应用

一、多态概述

1.1 什么是多态

多态:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。

1.2 多态的特点

  • 方法重写:子类重写父类方法
  • 类型兼容:父类引用指向子类对象
  • 动态绑定:运行时确定调用哪个方法

二、基本多态示例

2.1 简单的多态示例

class Animal:
    def make_sound(self):
        pass

class Dog(Animal):
    def make_sound(self):
        return "汪汪叫"

class Cat(Animal):
    def make_sound(self):
        return "喵喵叫"

def animal_sound(animal):
    print(animal.make_sound())

# 多态调用
dog = Dog()
cat = Cat()
animal_sound(dog)  # 输出:汪汪叫
animal_sound(cat)  # 输出:喵喵叫

2.2 多态的好处

# 不使用多态
def dog_sound(dog):
    print(dog.make_sound())

def cat_sound(cat):
    print(cat.make_sound())

# 使用多态
def animal_sound(animal):
    print(animal.make_sound())

# 代码更简洁,扩展性更好

三、方法重写与多态

3.1 方法重写实现多态

class Shape:
    def draw(self):
        print("绘制形状")
    
    def area(self):
        return 0

class Rectangle(Shape):
    def draw(self):
        print("绘制矩形")
    
    def area(self):
        return self.width * self.height

class Circle(Shape):
    def draw(self):
        print("绘制圆形")
    
    def area(self):
        return 3.14159 * self.radius ** 2

def process_shape(shape):
    shape.draw()
    print(f"面积: {shape.area()}")

3.2 多态的实际应用

class Payment:
    def pay(self, amount):
        pass

class CreditCard(Payment):
    def pay(self, amount):
        print(f"使用信用卡支付: {amount}元")

class WeChatPay(Payment):
    def pay(self, amount):
        print(f"使用微信支付: {amount}元")

class Alipay(Payment):
    def pay(self, amount):
        print(f"使用支付宝支付: {amount}元")

def make_payment(payment, amount):
    payment.pay(amount)

# 多态调用
payment = CreditCard()
make_payment(payment, 1000)

四、抽象基类与多态

4.1 使用抽象基类定义接口

from abc import ABC, abstractmethod

class Database(ABC):
    """数据库抽象基类"""
    
    @abstractmethod
    def connect(self):
        pass
    
    @abstractmethod
    def query(self, sql):
        pass
    
    @abstractmethod
    def close(self):
        pass

4.2 实现抽象类

class MySQLDatabase(Database):
    def connect(self):
        print("连接MySQL数据库")
    
    def query(self, sql):
        print(f"执行MySQL查询: {sql}")
    
    def close(self):
        print("关闭MySQL连接")

class PostgreSQLDatabase(Database):
    def connect(self):
        print("连接PostgreSQL数据库")
    
    def query(self, sql):
        print(f"执行PostgreSQL查询: {sql}")
    
    def close(self):
        print("关闭PostgreSQL连接")

4.3 使用多态

def use_database(database):
    database.connect()
    database.query("SELECT * FROM users")
    database.close()

# 可以切换不同的数据库实现
db = MySQLDatabase()
use_database(db)

db = PostgreSQLDatabase()
use_database(db)

五、鸭子类型

5.1 什么是鸭子类型

鸭子类型:如果它走起路来像鸭子,叫起来像鸭子,那么它就是鸭子。

5.2 鸭子类型示例

class Duck:
    def quack(self):
        print("嘎嘎叫")
    
    def walk(self):
        print("摇摇摆摆地走")

class Person:
    def quack(self):
        print("人在学鸭子叫")
    
    def walk(self):
        print("人在学鸭子走")

def duck_test(obj):
    obj.quack()
    obj.walk()

duck = Duck()
person = Person()

duck_test(duck)   # 输出:嘎嘎叫、摇摇摆摆地走
duck_test(person) # 输出:人在学鸭子叫、人在学鸭子走

5.3 鸭子类型的应用

# 不需要继承,只要实现相同的方法
class FileIterator:
    def __init__(self, filename):
        self.filename = filename
    
    def __iter__(self):
        with open(self.filename, 'r') as f:
            for line in f:
                yield line.strip()

class ListIterator:
    def __init__(self, items):
        self.items = items
    
    def __iter__(self):
        for item in self.items:
            yield item

# 可以统一处理不同的迭代器
def process_items(iterator):
    for item in iterator:
        print(item)

六、多态在集合中的应用

6.1 统一处理不同类型

class Vehicle:
    def move(self):
        pass

class Car(Vehicle):
    def move(self):
        print("汽车在公路上行驶")

class Boat(Vehicle):
    def move(self):
        print("船在水上航行")

class Airplane(Vehicle):
    def move(self):
        print("飞机在天空中飞行")

# 创建不同类型的交通工具集合
vehicles = [Car(), Boat(), Airplane()]

# 统一处理
for vehicle in vehicles:
    vehicle.move()

6.2 多态与方法重写

class Employee:
    def __init__(self, name):
        self.name = name
    
    def calculate_salary(self):
        return 0

class FullTimeEmployee(Employee):
    def __init__(self, name, base_salary):
        super().__init__(name)
        self.base_salary = base_salary
    
    def calculate_salary(self):
        return self.base_salary

class PartTimeEmployee(Employee):
    def __init__(self, name, hourly_rate, hours):
        super().__init__(name)
        self.hourly_rate = hourly_rate
        self.hours = hours
    
    def calculate_salary(self):
        return self.hourly_rate * self.hours

class CommissionEmployee(Employee):
    def __init__(self, name, base_salary, commission_rate, sales):
        super().__init__(name)
        self.base_salary = base_salary
        self.commission_rate = commission_rate
        self.sales = sales
    
    def calculate_salary(self):
        return self.base_salary + self.sales * self.commission_rate

# 多态计算薪资
employees = [
    FullTimeEmployee("张三", 8000),
    PartTimeEmployee("李四", 50, 80),
    CommissionEmployee("王五", 5000, 0.1, 20000)
]

for emp in employees:
    salary = emp.calculate_salary()
    print(f"{emp.name}: {salary}元")

七、多态与设计模式

7.1 策略模式

class SortStrategy:
    def sort(self, data):
        pass

class BubbleSort(SortStrategy):
    def sort(self, data):
        print("使用冒泡排序")
        return sorted(data)

class QuickSort(SortStrategy):
    def sort(self, data):
        print("使用快速排序")
        return sorted(data)

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)

# 使用不同的排序策略
sorter = Sorter(BubbleSort())
data = [5, 2, 8, 1, 9]
result = sorter.sort(data)

sorter.set_strategy(QuickSort())
result = sorter.sort(data)

7.2 工厂模式

class ShapeFactory:
    @staticmethod
    def create_shape(shape_type):
        if shape_type == "circle":
            return Circle()
        elif shape_type == "rectangle":
            return Rectangle()
        elif shape_type == "triangle":
            return Triangle()
        else:
            raise ValueError("未知的形状类型")

# 使用工厂创建对象
shape1 = ShapeFactory.create_shape("circle")
shape2 = ShapeFactory.create_shape("rectangle")

# 多态调用
shape1.draw()
shape2.draw()

八、多态的优势

8.1 代码复用

# 一个函数可以处理多种类型
def process_payment(payment_method, amount):
    payment_method.pay(amount)

# 支持多种支付方式
process_payment(CreditCard(), 1000)
process_payment(WeChatPay(), 1000)
process_payment(Alipay(), 1000)

8.2 扩展性

# 添加新的支付方式,不需要修改现有代码
class ApplePay(Payment):
    def pay(self, amount):
        print(f"使用Apple Pay支付: {amount}元")

# 现有代码可以直接使用
process_payment(ApplePay(), 1000)

8.3 灵活性

class Logger:
    def log(self, message):
        pass

class ConsoleLogger(Logger):
    def log(self, message):
        print(f"[Console] {message}")

class FileLogger(Logger):
    def log(self, message):
        with open("log.txt", "a") as f:
            f.write(f"[File] {message}\n")

class DatabaseLogger(Logger):
    def log(self, message):
        print(f"[Database] {message}")

# 可以灵活切换日志记录方式
def set_logger(logger):
    global current_logger
    current_logger = logger

def log_message(message):
    current_logger.log(message)

九、常见错误与注意事项

9.1 常见错误

  1. 忘记重写方法:
class Animal:
    def make_sound(self):
        pass

class Dog(Animal):
    # 忘记重写make_sound
    pass

dog = Dog()
dog.make_sound()  # 不会输出任何内容
  1. 方法签名不一致:
class Parent:
    def method(self, a, b):
        return a + b

class Child(Parent):
    def method(self, a):  # 参数不一致
        return a * 2

9.2 注意事项

  1. 确保子类重写所有抽象方法
  2. 保持方法签名一致
  3. 合理使用抽象基类定义接口
  4. 理解鸭子类型的适用场景

十、课后练习

练习题

  1. 编程题

    • 定义一个Shape抽象基类
    • 创建Rectangle、Circle、Triangle子类
    • 实现多态计算面积和周长
  2. 编程题

    • 定义一个Logger接口
    • 实现ConsoleLogger和FileLogger
    • 实现日志的切换
  3. 思考题

    • 多态的好处是什么?
    • 什么是鸭子类型?
    • 什么时候应该使用抽象基类?

十一、本集总结

核心知识点回顾

  1. 多态:同一操作作用于不同对象产生不同结果
  2. 方法重写:子类重写父类方法实现多态
  3. 抽象基类:定义接口规范
  4. 鸭子类型:不依赖继承,只依赖方法存在
  5. 动态绑定:运行时确定调用哪个方法

下集预告

下一集我们将学习类属性与实例属性,深入了解类属性和实例属性的区别与使用。


学习建议: 多态是面向对象编程的核心特性之一,理解多态对于编写灵活、可扩展的代码非常重要。通过实际练习加深理解。

« 上一篇 多继承 下一篇 » 类属性与实例属性