国庆节10月1日
--


多态的含义与写法(父类类型参数接收任意子类对象),以及"关注行为不关注类型"的鸭子类型

多态是指同一个方法,具有不同的形态、行为、表现。
1class Car:2 def charge(self):3 print('正在补充燃料...')4
5
6class FuelCar(Car):7 def charge(self):8 print('燃油汽车正在加油...')9
10
11class ElectricCar(Car):12 def charge(self):13 print('电动汽车正在充电...')14
15
16# 函数参数类型声明为父类17def handle_charge(car: Car):18 car.charge()19
20
21handle_charge(FuelCar()) # 燃油汽车正在加油...22handle_charge(ElectricCar()) # 电动汽车正在充电...关键写法:定义函数时参数类型指定为父类类型,执行时传入不同的子类对象,同一句 car.charge() 就表现出不同形态。
多态的价值在于调用方不用关心具体是哪种子类。以后新增”氢能车”,只要它也继承 Car 并实现 charge(),上面的 handle_charge 一行都不用改。
如果它走路像鸭子、叫起来像鸭子,那么它就是鸭子。
在鸭子类型中,我们关注的是对象的行为(它有什么方法),而不是对象的类型(它是什么类)。
1class Dog:2 def __init__(self, name, age):3 self.name = name4 self.age = age5
6 def swimming(self):7 print(f'{self.age} 岁的 {self.name} 正在游泳...')8
9
10class Duck:11 def __init__(self, name, age):12 self.name = name13 self.age = age14
15 def swimming(self):16 print(f'{self.age} 岁的 {self.name} 正在游泳...')17
18
19class Pig:20 def __init__(self, name, age):21 self.name = name22 self.age = age23
24 def swimming(self):25 print(f'{self.age} 岁的 {self.name} 正在游泳...')26
27
28def go_swimming(duck): # 参数没写类型,只要"会游泳"就行29 duck.swimming()30
31
32if __name__ == '__main__':33 go_swimming(Dog("旺财", 4))34 go_swimming(Duck("唐老鸭", 2))35 go_swimming(Pig("佩奇", 1))| 多态 | 鸭子类型 | |
|---|---|---|
| 关注点 | 父类 + 子类的继承体系 | 有没有那个方法(行为) |
| 是否需要继承 | 需要(子类继承父类) | 不需要 |
| 好处 | 统一调用入口,便于扩展 | 更灵活,任何对象只要能”做这件事”就能传进来 |
鸭子类型也可以看作多态的一种体现:关注的是对象的行为(有什么方法),而不是对象的类型(无需继承关系)。Python 作为动态类型语言,天然支持鸭子类型。
2-1 体会多态
定义 Payment 父类(方法 pay())和 WechatPay、AliPay 两个子类各自重写 pay();写一个函数 checkout(payment: Payment) 调用它,分别传入两种支付对象。
一级 · 思路:父类定义”能支付”,子类各自实现具体支付方式
二级 · 方法:函数形参写父类类型 payment: Payment,函数体只写 payment.pay()
三级 · 骨架:class WechatPay(Payment): 然后重写 pay
1# 2-1 多态:靠继承体系2class Payment:3 def pay(self):4 print("正在支付...")5
6
7class WechatPay(Payment):8 def pay(self):9 print("微信支付 100 元")10
11
12class AliPay(Payment):13 def pay(self):14 print("支付宝支付 100 元")15
16
17def checkout(payment: Payment):18 payment.pay()19
20
21checkout(WechatPay())22checkout(AliPay()) 2-2 体会鸭子类型
写一个 start_game(device) 函数,参数不限制类型,只要对象有 play() 方法就能传进来——分别传手机、电脑、掌机三个互不继承的类。
一级 · 思路:三个类毫无血缘关系,但都会 play
二级 · 方法:def start_game(device): device.play()
三级 · 骨架:如果传进去的对象没有 play(),会报 AttributeError——这正说明它检查的是”有没有方法”
1# 2-2 鸭子类型:不需要继承关系2class Phone:3 def play(self):4 print("手机:启动手游")5
6
7class Laptop:8 def play(self):9 print("电脑:启动端游")10
11
12class Console:13 def play(self):14 print("掌机:启动主机游戏")15
16
17def start_game(device):18 device.play()19
20
21start_game(Phone())22start_game(Laptop())23start_game(Console()) 2-3 两者对比
在 2-2 的基础上,故意传一个没有 play() 方法的对象,观察报错;再说出多态与鸭子类型的区别。
一级 · 思路:报错信息能帮你理解”鸭子类型靠行为兜底”
二级 · 方法:用 try/except AttributeError 捕获并打印友好提示
三级 · 骨架:多态靠继承体系统一入口;鸭子类型只要方法名对得上就行
1# 2-3:接在 2-2 的 start_game 后面(没有 play 方法的类)2class Fridge:3 pass4
5
6try:7 start_game(Fridge())8except AttributeError as e:9 print("启动失败:", e) # 'Fridge' object has no attribute 'play'如果你喜欢,那么欢迎来到我的世界!
了解更多暂未播放



"生活不是等待暴风雨过去,而是学会在雨中跳舞。"—— 塞维涅夫人


