Python多态
困难4多态:同一个指令,不同对象的不同反应
你有没有遇到过这样的场景:老师站在讲台上说“请站起来”,有的同学嗖地站起来,有的慢吞吞地站起来,还有的边站边打个哈欠。虽然都是“站起来”这个指令,但每个人执行的方式都不一样。在Python编程里,这种“同一个方法名,不同对象执行不同操作”的能力,就叫多态。
多态到底在说什么?
想象你走进动物学校,对一只猫说“叫一声”,它会“喵喵”;对一只狗说“叫一声”,它会“汪汪”。指令一模一样,但不同的动物给出了完全不同的回答。在代码里,我们经常需要写这样一个函数:它接收一个“动物”对象,然后调用它的“叫”方法。你不用关心这个动物到底是猫还是狗,只要它会叫就行——至于它怎么叫,那是它自己的事。这就是多态的核心理念:同一段代码,可以处理多种不同类型的对象,只要它们都具备相同的方法名。
生活中的多态
- 遥控器:按“开”键,电视打开屏幕,空调开始吹风,电饭煲开始煮饭。同一个“开”指令,每个电器都做出自己的响应。
- 绘画课:老师说“画一个图形吧”,小明画了圆形,小红画了正方形,小刚画了三角形。都是“画图形”,但结果五花八门。
- 游戏里的角色:在格斗游戏里,按下“攻击键”,拳击手会出拳,剑士会挥剑,魔法师会念咒语。程序只需要写一个
attack()方法,每个角色自己实现攻击方式。 - 打车App:你叫了“一辆车”,来的可能是轿车、SUV或出租车。它们都能“载你去目的地”,但座位数、舒适度不同。
多态的核心:继承 + 方法重写
在Python中,多态最常用的方式就是继承和方法重写。父类定义一个方法(比如make_sound),子类继承父类后,重新编写这个方法的具体内容。这样,当我们写一个函数时,它只关心对象有没有make_sound这个方法,而不关心它到底是哪个子类。
代码来啦:动物叫声
先创建一个动物基类,它有一个“发声”方法,但先不写具体内容(用pass跳过)。
class Animal:
def make_sound(self):
pass # 父类先占个位置,不做事
接下来,让小猫和小狗继承Animal,并重写make_sound方法:
class Cat(Animal):
def make_sound(self):
print("喵喵喵") # 猫的叫声
class Dog(Animal):
def make_sound(self):
print("汪汪汪") # 狗的叫声
现在写一个让动物叫的函数:
def animal_sound(animal):
animal.make_sound() # 不管是什么动物,直接让它叫
创建对象并测试:
cat = Cat() # 创建一只猫对象
dog = Dog() # 创建一只狗对象
animal_sound(cat) # 输出:喵喵喵
animal_sound(dog) # 输出:汪汪汪
看到了吗?同一个animal_sound函数,传入不同的动物对象,它们各自执行了自己的make_sound。这就是多态的典型用法。
多态的另一种形式:鸭子类型
Python有一种更灵活的多态,叫鸭子类型。这句话来自一个谚语:“如果它走起来像鸭子,叫起来像鸭子,那它就是鸭子。”意思就是:不要求对象必须继承自同一个父类,只要它有这个方法,就能用。
比如,我们有一个“飞行器”的模拟游戏。飞机和鸟都能飞,但它们没有共同的父类。
class Airplane:
def fly(self):
print("飞机在天空翱翔")
class Bird:
def fly(self):
print("小鸟拍打翅膀飞")
写一个函数让任何能飞的东西飞起来:
def let_it_fly(obj):
obj.fly() # 只要对象有 fly 方法就行
创建对象:
plane = Airplane() # 飞机对象
sparrow = Bird() # 小鸟对象
let_it_fly(plane) # 输出:飞机在天空翱翔
let_it_fly(sparrow) # 输出:小鸟拍打翅膀飞
注意:Airplane和Bird没有继承关系,但它们都有fly方法,所以能被同一个函数处理。这就是鸭子类型的魅力——代码更加灵活,不需要强行使用继承。
为什么要用多态?
- 代码更灵活:增加新动物(比如牛、羊、猪)时,不用修改
animal_sound函数,只需要创建新类、重写make_sound方法就行。 - 便于扩展:游戏里有很多角色,只要它们都有“攻击”方法,就可以用同一个循环来指挥所有角色攻击。
- 减少重复:如果每个动物都单独写一个函数(比如
cat_sound、dog_sound),代码会又长又乱。用多态,一个函数搞定所有。 - 维护方便:需要修改“叫”的逻辑时,只需改对应类的
make_sound方法,不会影响其他类。
新手容易犯的错误
错误1:忘记在子类中重写方法
如果你创建了一个Pig类,但忘记写make_sound方法,那么它就会继承父类的空方法(pass),调用时什么都不输出。
class Pig(Animal):
pass # 忘了重写,所以猪不会叫
pig = Pig()
animal_sound(pig) # 输出空,啥也没有
解决:记得在子类中重写父类的方法,并写上具体内容。
错误2:方法名写错(单词拼错)
比如把make_sound写成make_sond,这样就不是重写父类方法,而是新增了一个方法。调用animal_sound(pig)时仍然会调用父类的空方法。
class Pig(Animal):
def make_sond(self): # 拼写错误!不是重写
print("哼哼哼")
解决:确保子类方法名与父类完全一致(大小写也要相同)。
错误3:认为多态必须基于继承
有些同学觉得只有继承才能用多态,实际上Python的鸭子类型让多态更灵活。只要你传入的对象有对应的方法,就能工作,即使它们并没有共同的父类。
错误4:在继承关系中写了错误的参数
如果父类方法有参数,子类重写时参数必须一致(或者更灵活),否则调用时会报错。
完整示例:动物世界大联欢
下面是一段完整可运行的代码,包含猫、狗、猪,以及一个测试函数。你可以直接复制到Python环境中运行。
# 父类:动物
class Animal:
def make_sound(self):
pass # 父类先占位,不做事
# 子类:猫
class Cat(Animal):
def make_sound(self):
print("喵喵喵") # 猫的叫声
# 子类:狗
class Dog(Animal):
def make_sound(self):
print("汪汪汪") # 狗的叫声
# 子类:猪
class Pig(Animal):
def make_sound(self):
print("哼哼哼") # 猪的叫声
# 让动物叫的函数(多态核心)
def animal_sound(animal):
print(f"我听到:", end="") # 先打个招呼
animal.make_sound() # 调用对象的方法
# 创建各种动物对象
cat = Cat() # 一只猫
dog = Dog() # 一只狗
pig = Pig() # 一只猪
# 分别让它们叫
animal_sound(cat) # 输出:我听到:喵喵喵
animal_sound(dog) # 输出:我听到:汪汪汪
animal_sound(pig) # 输出:我听到:哼哼哼
小练习
- 创建一个
Cow(牛)类,让牛的叫声是“哞哞哞”,然后用animal_sound函数测试。 - 创建一个
Duck(鸭子)类,叫声是“嘎嘎嘎”,测试多态。 - 想一想:如果有一个
Robot(机器人)类,它也有make_sound方法(比如“滴滴滴”),但机器人不是动物,能直接传给animal_sound函数吗?试试看。
相关知识点指引
- 继承:子类拥有父类的属性和方法,可以在此基础上扩展。多态常常依赖继承。
- 方法重写(覆盖):子类重新实现父类中已有的方法,是达成多态的关键。
- 鸭子类型:不检查类型,只关心对象有没有需要的方法,Python中的多态常以此形式出现。
- 抽象基类:在更复杂的项目中,可以用
abc模块强制子类必须实现某些方法,避免忘记重写。 - isinstance()函数:有时需要判断对象是不是某个类的实例,但多态一般不需要这个函数——直接调用方法更优雅。
多态是面向对象编程的三大特性之一(封装、继承、多态),掌握它之后,你的代码会变得更灵活、更易扩展。下次写程序时,试着先定义一个通用的方法名,然后让不同的对象用自己的方式去实现它吧!
例题精讲
下列Python代码中,最能体现多态特性的是?
执行以下代码,输出结果是?\nclass Duck:\n def quack(self):\n return \"呱呱\"\nclass Person:\n def quack(self):\n return \"假装我是鸭子\"\ndef make_sound(obj):\n print(obj.quack())\nduck = Duck()\nperson = Person()\nmake_sound(duck)\nmake_sound(person)
在Python中,多态只能通过继承和重写父类方法来实现。
请补全以下代码,使程序能利用多态调用不同动物的叫声。\nclass Animal:\n def speak(self):\n pass\nclass Dog(Animal):\n def speak(self):\n return \"汪汪\"\nclass Cat(Animal):\n def speak(self):\n return \"喵喵\"\ndef animal_sound(___):\n print(animal.speak())\nanimal_sound(Dog())\nanimal_sound(Cat())下面的代码尝试使用鸭子类型实现多态,但缺少关键一行。请补全。\ndef start(device):\n ___\nclass USB:\n def connect(self):\n print(\"USB已连接\")\nclass HDMI:\n def connect(self):\n print(\"HDMI已连接\")\nstart(USB())\nstart(HDMI())