Skip to content

多态是什么 ​

多态是指同一个方法,具有不同的形态、行为、表现。

python
class Car:
    def charge(self):
        print('正在补充燃料...')


class FuelCar(Car):
    def charge(self):
        print('燃油汽车正在加油...')


class ElectricCar(Car):
    def charge(self):
        print('电动汽车正在充电...')


# 函数参数类型声明为父类
def handle_charge(car: Car):
    car.charge()


handle_charge(FuelCar())        # 燃油汽车正在加油...
handle_charge(ElectricCar())    # 电动汽车正在充电...

关键写法:定义函数时参数类型指定为父类类型,执行时传入不同的子类对象,同一句 car.charge() 就表现出不同形态。

TIP

多态的价值在于调用方不用关心具体是哪种子类。以后新增"氢能车",只要它也继承 Car 并实现 charge(),上面的 handle_charge 一行都不用改。

鸭子类型(Duck Typing) ​

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

在鸭子类型中,我们关注的是对象的行为(它有什么方法),而不是对象的类型(它是什么类)。

python
class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def swimming(self):
        print(f'{self.age} 岁的 {self.name} 正在游泳...')


class Duck:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def swimming(self):
        print(f'{self.age} 岁的 {self.name} 正在游泳...')


class Pig:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def swimming(self):
        print(f'{self.age} 岁的 {self.name} 正在游泳...')


def go_swimming(duck):      # 参数没写类型,只要"会游泳"就行
    duck.swimming()


if __name__ == '__main__':
    go_swimming(Dog("旺财", 4))
    go_swimming(Duck("唐老鸭", 2))
    go_swimming(Pig("佩奇", 1))
多态鸭子类型
关注点父类 + 子类的继承体系有没有那个方法(行为)
是否需要继承需要(子类继承父类)不需要
好处统一调用入口,便于扩展更灵活,任何对象只要能"做这件事"就能传进来

NOTE

鸭子类型也可以看作多态的一种体现:关注的是对象的行为(有什么方法),而不是对象的类型(无需继承关系)。Python 作为动态类型语言,天然支持鸭子类型。

相关 ​

练习题 ​

一、知识回顾(读完直接做下面的实践题) ​

  1. 多态是指同一个方法,具有不同的形态、行为、表现
  2. 实现多态的常见写法:函数参数类型指定为父类类型,执行时传入不同的子类对象
  3. 鸭子类型的说法是"如果它走路像鸭子、叫起来像鸭子,那么它就是鸭子"
  4. 鸭子类型关注的是对象的行为(有什么方法),而不是对象的类型(是什么类)
  5. 鸭子类型不需要存在继承关系
  6. 多态的好处:调用方不用关心具体的子类类型,新增子类时调用代码不用改

二、裸写题 ​

  • - 提示(先自己想,实在想不出再点开)

    一级 · 思路:父类定义"能支付",子类各自实现具体支付方式 二级 · 方法:函数形参写父类类型 payment: Payment,函数体只写 payment.pay()三级 · 骨架:class WechatPay(Payment): 然后重写 pay

    - 参考答案(做完再点开)

    python
    # 2-1 多态:靠继承体系
    class Payment:
        def pay(self):
            print("正在支付...")
    
    
    class WechatPay(Payment):
        def pay(self):
            print("微信支付 100 元")
    
    
    class AliPay(Payment):
        def pay(self):
            print("支付宝支付 100 元")
    
    
    def checkout(payment: Payment):
        payment.pay()
    
    
    checkout(WechatPay())
    checkout(AliPay())
  • - 提示(先自己想,实在想不出再点开)

    一级 · 思路:三个类毫无血缘关系,但都会 play 二级 · 方法:def start_game(device): device.play()三级 · 骨架:如果传进去的对象没有 play(),会报 AttributeError——这正说明它检查的是"有没有方法"

    - 参考答案(做完再点开)

    python
    # 2-2 鸭子类型:不需要继承关系
    class Phone:
        def play(self):
            print("手机:启动手游")
    
    
    class Laptop:
        def play(self):
            print("电脑:启动端游")
    
    
    class Console:
        def play(self):
            print("掌机:启动主机游戏")
    
    
    def start_game(device):
        device.play()
    
    
    start_game(Phone())
    start_game(Laptop())
    start_game(Console())
  • - 提示(先自己想,实在想不出再点开)

    一级 · 思路:报错信息能帮你理解"鸭子类型靠行为兜底" 二级 · 方法:用 try/except AttributeError 捕获并打印友好提示 三级 · 骨架:多态靠继承体系统一入口;鸭子类型只要方法名对得上就行

    - 参考答案(做完再点开)

    python
    # 2-3:接在 2-2 的 start_game 后面(没有 play 方法的类)
    class Fridge:
        pass
    
    
    try:
        start_game(Fridge())
    except AttributeError as e:
        print("启动失败:", e)   # 'Fridge' object has no attribute 'play'