第25课:Python|方法重写、多态、抽象类与接口Python完整实现

在这里插入图片描述


📖 开篇导读

在前几节课中,我们学习了面向对象的两大特性:封装(第23课)和继承(第24课)。今天我们将迎来面向对象三大特性的最后一位——多态

多态(Polymorphism)源自希腊语,意为“多种形态”。在编程中,多态指的是同一个方法调用,由于对象不同,可能表现出不同的行为。它让代码更具灵活性和扩展性,是许多设计模式的基础。

💡 工作场景:图形系统中,draw()方法在CircleRectangleTriangle中有不同实现;支付系统中,pay()方法在AlipayWechatPay中各有逻辑;游戏开发中,attack()方法在WarriorMageArcher中有不同效果。多态让你写出通用代码,无需关心具体类型。

Python的多态与其他语言略有不同:它是“鸭子类型”的——只要对象有相应的方法,就可以调用,不要求继承关系。此外,Python提供了**抽象基类(ABC)**来定义接口,强制子类实现特定方法。

本课将学习:

  • 方法重写的原理和应用
  • 多态的概念及Python中的实现方式
  • 抽象基类(ABC)与抽象方法
  • 接口的模拟与@abstractmethod
  • 实战:图形系统、支付系统、插件架构

学完本课,你将能够设计出高内聚、低耦合的可扩展系统。


🎯 学习目标

目标编号具体掌握内容对应面试/工作价值
1️⃣理解方法重写的实质与super()的协作机制扩展父类功能
2️⃣掌握多态的概念,区分重写与重载面试高频“多态与重载区别”
3️⃣深入理解鸭子类型及Python的多态实现方式写出更Pythonic的代码
4️⃣熟练使用abc模块定义抽象基类和抽象方法设计接口,约束子类
5️⃣理解抽象类接口在Python中的统一实现大型项目架构
6️⃣实战:实现一个基于抽象基类的插件系统掌握可扩展设计

🔥 面试考点:“什么是多态?Python如何实现多态?”“抽象类和接口的区别?”“鸭子类型是什么?”“什么时候使用抽象基类?”


📚 知识点理论精讲

一、方法重写(Override)再探

1.1 重写的本质

子类重新定义父类中已有的方法,称为方法重写。在Python中,所有方法都是虚方法(可被重写)。

class Parent:
    def method(self):
        print("Parent")

class Child(Parent):
    def method(self):
        print("Child")   # 重写

c = Child()
c.method()  # Child

1.2 扩展而非覆盖

通常重写时,我们希望在子类逻辑之外,仍然执行父类的部分逻辑。使用super()可以达到扩展效果。

class Animal:
    def speak(self):
        print("动物发出声音")

class Dog(Animal):
    def speak(self):
        super().speak()   # 先调用父类
        print("汪汪")     # 再添加子类行为

d = Dog()
d.speak()

1.3 重写与重载的区别

  • 重写(Override):子类重新定义父类的方法,方法名、参数列表相同。
  • 重载(Overload):同一个类中定义多个同名但参数列表不同的方法。Python不支持传统的方法重载,但可以通过默认参数、可变参数或使用functools.singledispatch实现类似效果。
# Python中的“重载”模拟
class Math:
    def add(self, a, b, c=None):
        if c is None:
            return a + b
        return a + b + c

二、多态:同一接口,多种形态

2.1 多态的基本概念

多态允许不同类型的对象对同一消息作出不同响应。在面向对象编程中,多态通常通过继承和虚方法实现。

class Shape:
    def area(self):
        pass

class Circle(Shape):
    def __init__(self, r):
        self.r = r
    def area(self):
        return 3.14 * self.r ** 2

class Rectangle(Shape):
    def __init__(self, w, h):
        self.w = w
        self.h = h
    def area(self):
        return self.w * self.h

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

c = Circle(5)
r = Rectangle(4, 6)
print_area(c)   # 面积: 78.5
print_area(r)   # 面积: 24

2.2 鸭子类型(Duck Typing)

Python的多态不要求显式的继承关系。只要一个对象有符合要求的方法,就可以使用。这就是“鸭子类型”:如果它走起来像鸭子,叫起来像鸭子,那它就是鸭子。

class Duck:
    def quack(self):
        print("嘎嘎")

class Person:
    def quack(self):
        print("模仿鸭子嘎嘎")

def make_quack(obj):
    obj.quack()

make_quack(Duck())    # 嘎嘎
make_quack(Person())  # 模仿鸭子嘎嘎

💡 工作应用:鸭子类型让代码更灵活,你不需要为了复用而设计复杂的继承体系。只要对象实现了所需的方法,就可以传入。

2.3 多态的优势

  • 开闭原则:对扩展开放,对修改关闭。新增类型不需要修改原有函数。
  • 代码复用:通用逻辑写在高层,具体实现写在底层。
  • 可维护性:接口统一,降低耦合。

三、抽象基类(Abstract Base Class, ABC)

抽象基类不能实例化,用于定义一组必须被实现的接口(抽象方法)。Python通过abc模块提供支持。

3.1 定义抽象基类

from abc import ABC, abstractmethod

class Animal(ABC):
    @abstractmethod
    def speak(self):
        pass

class Dog(Animal):
    def speak(self):
        print("汪汪")

# a = Animal()  # TypeError: Can't instantiate abstract class Animal
d = Dog()
d.speak()

3.2 抽象方法可以有实现

抽象方法也可以有默认实现,子类可以不重写,但如果重写,需要调用super()来使用默认逻辑。

class Vehicle(ABC):
    @abstractmethod
    def start(self):
        print("车辆启动中...")
    
    @abstractmethod
    def stop(self):
        pass

class Car(Vehicle):
    def start(self):
        super().start()
        print("汽车引擎点火")
    
    def stop(self):
        print("汽车熄火")

3.3 @abstractmethod@property结合

可以定义抽象属性。

from abc import ABC, abstractmethod

class Person(ABC):
    @property
    @abstractmethod
    def name(self):
        pass

class Student(Person):
    def __init__(self, name):
        self._name = name
    
    @property
    def name(self):
        return self._name

3.4 抽象基类与register注册虚拟子类

可以使用register方法将一个类注册为抽象基类的“虚拟子类”,即使它没有显式继承。isinstanceissubclass会返回True,但虚拟子类不需要实现抽象方法。

class Duck:
    def quack(self):
        print("嘎嘎")

Animal.register(Duck)   # 将Duck注册为Animal的虚拟子类
print(issubclass(Duck, Animal))  # True
duck = Duck()
print(isinstance(duck, Animal))  # True

四、接口的模拟

在其他语言(如Java)中,接口(Interface)是一种纯抽象类,只能包含方法签名,不能有实现。Python没有专门的接口关键字,但可以通过抽象基类模拟。

4.1 定义“接口”(纯抽象类)

from abc import ABC, abstractmethod

class Drawable(ABC):
    @abstractmethod
    def draw(self):
        pass
    
    @abstractmethod
    def get_size(self):
        pass

class Circle(Drawable):
    def draw(self):
        print("画圆")
    def get_size(self):
        return (10, 10)

4.2 多重“实现”接口

一个类可以继承多个抽象基类(接口)。

class Serializable(ABC):
    @abstractmethod
    def to_dict(self):
        pass

class Drawable(ABC):
    @abstractmethod
    def draw(self):
        pass

class Shape(Circle, Serializable):
    pass   # 需要实现所有抽象方法

五、多态与抽象基类的实战模式

5.1 策略模式(简单版)

from abc import ABC, abstractmethod

class SortStrategy(ABC):
    @abstractmethod
    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 Context:
    def __init__(self, strategy: SortStrategy):
        self.strategy = strategy
    def execute_sort(self, data):
        return self.strategy.sort(data)

data = [3,1,4,2]
ctx = Context(BubbleSort())
ctx.execute_sort(data)

5.2 插件系统架构

import importlib

class Plugin(ABC):
    @abstractmethod
    def run(self, context):
        pass

class PluginManager:
    def __init__(self):
        self.plugins = []
    
    def register(self, plugin: Plugin):
        self.plugins.append(plugin)
    
    def run_all(self, context):
        for plugin in self.plugins:
            plugin.run(context)

💻 代码案例实操

案例1:方法重写与扩展

"""
override_demo.py
演示方法重写的各种模式
"""

class Logger:
    def log(self, message, level="INFO"):
        print(f"[{level}] {message}")

class TimestampLogger(Logger):
    from datetime import datetime
    def log(self, message, level="INFO"):
        timestamp = self.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        super().log(f"{timestamp} - {message}", level)

class ErrorLogger(TimestampLogger):
    def log(self, message, level="INFO"):
        # 如果是错误级别,强制转换为ERROR
        if "error" in message.lower():
            level = "ERROR"
        super().log(message, level)

# 使用
logger = ErrorLogger()
logger.log("系统启动")
logger.log("出现error", "WARNING")  # 级别会被强制转为ERROR

案例2:多态与鸭子类型

"""
polymorphism_demo.py
演示多态和鸭子类型
"""

# 使用继承的多态
class Bird:
    def fly(self):
        print("鸟飞")

class Airplane:
    def fly(self):
        print("飞机飞")

class Superman:
    def fly(self):
        print("超人飞")

def make_it_fly(obj):
    # 鸭子类型:不关心是什么,只关心有没有fly方法
    obj.fly()

bird = Bird()
plane = Airplane()
superman = Superman()
make_it_fly(bird)
make_it_fly(plane)
make_it_fly(superman)

# 另一个例子:不同的对象,相同的接口
class Cat:
    def sound(self):
        return "喵"

class Dog:
    def sound(self):
        return "汪"

class Cow:
    def sound(self):
        return "哞"

animals = [Cat(), Dog(), Cow()]
for animal in animals:
    print(animal.sound())

案例3:抽象基类强制实现方法

"""
abc_force.py
使用抽象基类强制子类实现必要方法
"""

from abc import ABC, abstractmethod

class DataProcessor(ABC):
    @abstractmethod
    def load_data(self):
        pass
    
    @abstractmethod
    def process(self):
        pass
    
    @abstractmethod
    def save_result(self):
        pass
    
    def run(self):
        self.load_data()
        self.process()
        self.save_result()
        print("处理完成")

class CsvProcessor(DataProcessor):
    def load_data(self):
        print("加载CSV文件")
    
    def process(self):
        print("处理CSV数据")
    
    def save_result(self):
        print("保存结果为CSV")

class JsonProcessor(DataProcessor):
    def load_data(self):
        print("加载JSON文件")
    
    def process(self):
        print("处理JSON数据")
    
    def save_result(self):
        print("保存结果为JSON")

# processor = DataProcessor()  # TypeError
csv = CsvProcessor()
csv.run()
json = JsonProcessor()
json.run()

案例4:抽象属性与抽象类方法

"""
abc_property.py
演示抽象属性和抽象类方法
"""

from abc import ABC, abstractmethod

class Config(ABC):
    @property
    @abstractmethod
    def host(self):
        pass
    
    @property
    @abstractmethod
    def port(self):
        pass
    
    @classmethod
    @abstractmethod
    def default(cls):
        pass

class DevelopmentConfig(Config):
    @property
    def host(self):
        return "localhost"
    
    @property
    def port(self):
        return 8080
    
    @classmethod
    def default(cls):
        return cls()

class ProductionConfig(Config):
    @property
    def host(self):
        return "prod.example.com"
    
    @property
    def port(self):
        return 80
    
    @classmethod
    def default(cls):
        return cls()

def run_server(config: Config):
    print(f"启动服务器 {config.host}:{config.port}")

run_server(DevelopmentConfig.default())
run_server(ProductionConfig.default())

案例5:虚拟子类注册

"""
virtual_subclass.py
演示注册虚拟子类,让已有类“属于”抽象基类
"""

from abc import ABC, abstractmethod

class Iterable(ABC):
    @abstractmethod
    def __iter__(self):
        pass

# 注册list为Iterable的虚拟子类
Iterable.register(list)

print(issubclass(list, Iterable))   # True
print(isinstance([1,2,3], Iterable)) # True

# 自定义一个类,不继承Iterable,但注册后也会被认为是子类
class MyCollection:
    def __iter__(self):
        return iter([1,2,3])

Iterable.register(MyCollection)

mc = MyCollection()
print(isinstance(mc, Iterable))   # True

# 但实际调用时,如果MyCollection没有实现 __iter__ 会怎样?
# 注册只影响isinstance/issubclass,不影响实际调用

案例6:支付系统(多态+抽象基类)

"""
payment_system.py
模拟支付系统,演示抽象基类和多态
"""

from abc import ABC, abstractmethod
import random

class PaymentMethod(ABC):
    @abstractmethod
    def pay(self, amount):
        pass
    
    @abstractmethod
    def refund(self, amount):
        pass

class Alipay(PaymentMethod):
    def pay(self, amount):
        print(f"支付宝支付 {amount} 元")
        # 模拟支付请求
        return True
    
    def refund(self, amount):
        print(f"支付宝退款 {amount} 元")
        return True

class WechatPay(PaymentMethod):
    def pay(self, amount):
        print(f"微信支付 {amount} 元")
        return True
    
    def refund(self, amount):
        print(f"微信退款 {amount} 元")
        return True

class CreditCard(PaymentMethod):
    def __init__(self, card_number):
        self.card_number = card_number
    
    def pay(self, amount):
        if random.random() > 0.95:
            print("信用卡支付失败(模拟网络错误)")
            return False
        print(f"信用卡 {self.card_number[-4:]} 支付 {amount} 元")
        return True
    
    def refund(self, amount):
        print(f"信用卡 {self.card_number[-4:]} 退款 {amount} 元")
        return True

class Order:
    def __init__(self, order_id, amount):
        self.order_id = order_id
        self.amount = amount
        self.paid = False
    
    def process_payment(self, payment_method: PaymentMethod):
        if payment_method.pay(self.amount):
            self.paid = True
            print(f"订单 {self.order_id} 支付成功")
        else:
            print(f"订单 {self.order_id} 支付失败")

# 使用
order1 = Order("1001", 299)
order2 = Order("1002", 599)

alipay = Alipay()
wechat = WechatPay()
credit = CreditCard("1234-5678-9012-3456")

order1.process_payment(alipay)
order2.process_payment(credit)

案例7:图形编辑器(综合多态)

"""
graphic_editor.py
图形编辑器,使用多态实现绘制、移动、缩放
"""

from abc import ABC, abstractmethod
import math

class Shape(ABC):
    @abstractmethod
    def draw(self):
        pass
    
    @abstractmethod
    def move(self, dx, dy):
        pass
    
    @abstractmethod
    def area(self):
        pass

class Circle(Shape):
    def __init__(self, x, y, radius):
        self.x = x
        self.y = y
        self.radius = radius
    
    def draw(self):
        print(f"画圆 圆心=({self.x},{self.y}) 半径={self.radius}")
    
    def move(self, dx, dy):
        self.x += dx
        self.y += dy
        print(f"圆形移动到 ({self.x},{self.y})")
    
    def area(self):
        return math.pi * self.radius ** 2

class Rectangle(Shape):
    def __init__(self, x, y, width, height):
        self.x = x
        self.y = y
        self.width = width
        self.height = height
    
    def draw(self):
        print(f"画矩形 左上角=({self.x},{self.y}) 宽={self.width} 高={self.height}")
    
    def move(self, dx, dy):
        self.x += dx
        self.y += dy
        print(f"矩形移动到 ({self.x},{self.y})")
    
    def area(self):
        return self.width * self.height

class Triangle(Shape):
    def __init__(self, x1, y1, x2, y2, x3, y3):
        self.points = [(x1, y1), (x2, y2), (x3, y3)]
    
    def draw(self):
        print(f"画三角形 顶点={self.points}")
    
    def move(self, dx, dy):
        self.points = [(x+dx, y+dy) for x, y in self.points]
        print(f"三角形移动到新顶点={self.points}")
    
    def area(self):
        # 使用鞋带公式计算多边形面积
        x = [p[0] for p in self.points]
        y = [p[1] for p in self.points]
        return 0.5 * abs(sum(x[i]*y[(i+1)%3] - x[(i+1)%3]*y[i] for i in range(3)))

class Editor:
    def __init__(self):
        self.shapes = []
    
    def add_shape(self, shape: Shape):
        self.shapes.append(shape)
    
    def draw_all(self):
        for shape in self.shapes:
            shape.draw()
    
    def move_all(self, dx, dy):
        for shape in self.shapes:
            shape.move(dx, dy)
    
    def total_area(self):
        return sum(shape.area() for shape in self.shapes)

# 使用
editor = Editor()
editor.add_shape(Circle(10, 10, 5))
editor.add_shape(Rectangle(20, 20, 30, 40))
editor.add_shape(Triangle(0,0, 10,0, 5,10))

editor.draw_all()
print(f"总面积: {editor.total_area():.2f}")
editor.move_all(5, 5)
editor.draw_all()

⚠️ 易错点避坑总结

序号坑点描述后果解决方案
1重写方法时忘记调用super(),导致父类初始化逻辑未执行父类属性未设置,可能AttributeError必要时在子类__init__等中调用super()
2在抽象基类中定义了抽象方法,但子类未实现实例化子类时TypeError确保所有抽象方法都被实现
3以为抽象基类可以像接口一样多重继承,但多个抽象基类可能有同名抽象方法子类需要同时实现,可能导致冲突合理设计方法名,或使用组合
4滥用鸭子类型,没有文档或类型提示,导致调用方传入错误类型的对象运行时AttributeError可以使用类型注解(typing.Protocol)或抽象基类进行类型检查
5在抽象方法中编写了实现,但子类忘记调用super()父类的默认实现未执行在文档中说明是否需要调用super()
6使用isinstance检查具体类型,而不是接口违反开闭原则,添加新类型需要修改代码使用isinstance(obj, AbstractClass)或鸭子类型
7抽象基类中混合了具体方法和抽象方法,子类容易遗漏抽象方法子类仍可实例化?不,只要有抽象方法就不可实例化定期检查抽象基类,或使用工具
8注册虚拟子类后,以为自动获得了抽象方法实现运行时调用未实现的方法会出错注册只是类型检查的标记,不提供实现
9多继承抽象基类时,MRO导致方法解析顺序异常调用父类方法不如预期查看__mro__,使用super()
10在抽象属性setter中只定义了getter子类如果没有实现setter,但需要设置值时会出错抽象属性通常只定义getter,或者同时定义setter为抽象

📝 课后实战练习题

第1题:方法重写练习

定义一个Vehicle基类,有start()stop()方法,打印通用信息。定义CarBike子类,重写start()stop(),分别输出“汽车启动/熄火”、“自行车骑行/刹车”。在子类中调用父类方法后添加特有逻辑。创建实例测试。

第2题:多态与函数

编写一个函数describe(obj),接收任意对象,如果对象有description方法就调用它,否则返回该对象的字符串表示。演示多态行为。

第3题:抽象基类实现数据库驱动

定义抽象基类DatabaseDriver,包含connect()query(sql)close()抽象方法。实现两个子类:MySQLDriverSQLiteDriver,分别模拟(print代替实际连接)。编写一个函数execute_query(driver, sql),接收驱动对象并执行查询。

第4题:虚拟子类练习

定义抽象基类Iterable,带__iter__抽象方法。然后定义MyCollection类,实现了__iter__但不继承Iterable。使用register将其注册为虚拟子类。验证isinstanceissubclass。然后尝试迭代。

第5题:图形系统扩展

在案例7的图形编辑器中,新增Square类,继承自RectangleShape,实现相应方法。并在Editor中添加group_shapes功能(组合模式):创建一个Group类,也继承Shape,包含多个Shape,其movedraw方法委托给内部形状。测试组合。

第6题:支付系统添加新方式

在案例6的支付系统中,添加PayPal支付方式,包含email属性。实现payrefund方法,模拟调用API。修改Order类,使其支持接收多种支付方式,并统计支付总金额。

第7题:插件系统

设计一个简单的插件系统:

  • 定义Plugin抽象基类,包含execute()抽象方法。
  • 创建两个插件:HelloPluginTimePlugin
  • 实现PluginManager,能够动态加载插件(可以手动添加,或扫描某个目录)。
  • 实现一个命令行界面,让用户选择执行哪个插件。

🔜 下节课预告

面向对象的三大特性已全部讲完:封装、继承、多态。接下来我们将深入类的内部:类属性、实例属性、类方法、静态方法的应用场景,以及一些高级技巧。

第26课:类属性、实例属性、类方法、静态方法应用场景全解

内容包括:

  • 类属性与实例属性的深入对比与最佳实践
  • 类方法作为工厂方法
  • 静态方法作为工具函数组织
  • property的高级用法
  • 实战:计数器、配置管理器

学完本课,你将能熟练选用合适的属性和方法类型,写出更清晰的面向对象代码。


🌟 学习鼓励:多态是面向对象的高级特性,它让代码能够优雅地应对变化。理解鸭子类型,你会发现自己不再需要写大量的isinstance判断。抽象基类是大型项目的“契约”,掌握它你就能设计出稳定的API。多做练习,把这些概念内化为编程本能!


🔗《50节课 Python 从入门到精通》系列课程导航

去订阅

🌟 感谢您耐心阅读到这里!
💡 如果本文对您有所启发欢迎:
👍 点赞📌 收藏 📤 分享给更多需要的伙伴。
🗣️ 期待在评论区看到您的想法, 共同进步。
🔔 关注我,持续获取更多干货内容~
🤗 我们下篇文章见~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Thomas.Sir

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值