多态是什么
多态是指同一个方法,具有不同的形态、行为、表现。
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)
如果它走路像鸭子、叫起来像鸭子,那么它就是鸭子。
在鸭子类型中,我们关注的是对象的行为(它有什么方法),而不是对象的类型(它是什么类)。
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 作为动态类型语言,天然支持鸭子类型。
相关
练习题
一、知识回顾(读完直接做下面的实践题)
- 多态是指同一个方法,具有不同的形态、行为、表现
- 实现多态的常见写法:函数参数类型指定为父类类型,执行时传入不同的子类对象
- 鸭子类型的说法是"如果它走路像鸭子、叫起来像鸭子,那么它就是鸭子"
- 鸭子类型关注的是对象的行为(有什么方法),而不是对象的类型(是什么类)
- 鸭子类型不需要存在继承关系
- 多态的好处:调用方不用关心具体的子类类型,新增子类时调用代码不用改
二、裸写题
- 提示(先自己想,实在想不出再点开)
一级 · 思路:父类定义"能支付",子类各自实现具体支付方式 二级 · 方法:函数形参写父类类型
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'
