多态(Polymorphism)是面向对象编程的三大核心特性之一(封装、继承、多态)。同一个方法或操作符,在不同的对象上可以有不同的行为。

多态的核心思想

核心思想是:你不需要关心一个对象具体是什么类型,只需要关心它有没有你要的方法或属性(即它是否“像”你需要的那个东西)。如果有,它就可以被调用。

这也就是著名的“鸭子测试”:“如果它走起路来像鸭子,叫起来也像鸭子,那么它就是鸭子。”

多态的实现

在 Python 中,多态通常通过以下两种方式实现:

1、继承与方法重写

子类继承父类后,可以重写(Override)父类的方法,从而使得相同的方法名在不同的子类中有不同的实现。

class Animal:
    def speak(self):
        # 父类的方法通常不实现具体功能,或者提供一个基础实现/抛异常
        raise NotImplementedError("子类必须实现此方法")

class Dog(Animal):
    def speak(self): # 重写父类的 speak 方法
        return "汪汪!"

class Cat(Animal):
    def speak(self): # 重写父类的 speak 方法
        return "喵喵!"

class Duck(Animal):
    def speak(self): # 重写父类的 speak 方法
        return "嘎嘎!"

# 创建一个函数,它接受一个 Animal 类型的对象(或其子类)
def animal_talk(animal_obj):
    # 我们不关心 animal_obj 具体是哪种动物
    # 我们只关心它是否有 .speak() 方法
    print(animal_obj.speak())

# 创建不同的对象
my_dog = Dog()
my_cat = Cat()
my_duck = Duck()

# 调用同一个函数,传入不同的对象,产生不同的行为(多种形态)
animal_talk(my_dog)  # 输出: 汪汪!
animal_talk(my_cat)  # 输出: 喵喵!
animal_talk(my_duck) # 输出: 嘎嘎!

2、鸭子类型(Duck Typing)

Python 的多态不依赖于严格的继承关系。即使两个类完全没有继承同一个父类,只要它们有相同名称的方法,它们就可以被同样地对待。

class Dog:
    def speak(self):
        return "汪汪!"

class Robot:
    # 一个机器人,它根本不是 Animal 的子类
    def speak(self):
        return "你好,主人!我是机器人。"

class Car:
    # 一辆汽车,没有 speak 方法
    def run(self):
        return "引擎轰鸣!"

# 同样的函数,不关心类型,只关心行为
def make_it_speak(entity):
    print(entity.speak())

my_dog = Dog()
my_robot = Robot()
my_car = Car() # 这个对象没有 speak 方法

make_it_speak(my_dog)    # 输出: 汪汪!
make_it_speak(my_robot)  # 输出: 你好,主人!我是机器人。

# 尝试对 Car 调用会出错
make_it_speak(my_car) # AttributeError: 'Car' object has no attribute 'speak'

多态的优势

1、提高灵活性:代码可以处理多种不同类型的对象,只要它们遵循相同的接口(具有相同的方法名)。

2、提高可扩展性:如果要增加一个新的类,只需要确保这个新类实现了所需的方法(例如 speak),那么现有的函数(例如 animal_talk 或 make_it_speak)就可以立即处理这个新类的对象,而不需要修改函数本身的代码。这符合“开闭原则”(对扩展开放,对修改封闭)。

3、简化代码:使用者不需要写一大堆 if isinstance(obj, Dog)…else if isinstance(obj, Cat)… 这样的类型检查语句,代码更简洁、更通用。

与抽象基类 (ABC) 的结合

为了防止出现错误,我们会使用抽象基类来定义一种“协议”或“接口”,明确要求子类必须实现某些方法。

from abc import ABC, abstractmethod

class Speaker(ABC): # 继承 ABC,这是一个抽象基类
    @abstractmethod
    def speak(self):
        pass # 抽象方法,没有实现

class Dog(Speaker): # 现在 Dog 必须实现 speak
    def speak(self):
        return "汪汪!"

class Cat(Speaker): # Cat 也必须实现 speak
    def speak(self):
        return "喵喵!"

# 如果有一个类继承 Speaker 但没有实现 speak,在实例化时就会报错
class InvalidSpeaker(Speaker):
    pass

# invalid = InvalidSpeaker() # 这行会报错:TypeError: Can't instantiate abstract class InvalidSpeaker with abstract method speak

抽象基类提供了一个严格的契约,但它仍然支持多态。任何继承自 Speaker 的类都保证有 speak 方法,因此 make_it_speak 函数可以安全地调用它。

更多推荐