在 Python 中,多态指同一调用形式作用于不同对象时可产生不同行为。语言采用鸭子类型:不强制共享某一父类,只要对象提供所需方法,即可被同一段代码调用。若需一组类型遵守统一接口,亦可在父类中约定方法名并由子类实现。本节说明鸭子类型、继承场景下的多态及内置函数中的协议。
同名方法,不同行为
make_speak 仅要求参数具有 speak 方法,不区分具体类型。传入不同实例时,输出不同:
class Dog:
def speak(self):
print("汪汪!")
class Cat:
def speak(self):
print("喵喵!")
def make_speak(animal):
animal.speak()
make_speak(Dog())
make_speak(Cat())鸭子类型
能否使用某对象,取决于是否具备所需行为而非名义上的类型关系。Car、Bike、Bird 无共同父类,但均实现 move,即可传入同一 go 函数:
class Car:
def move(self):
print("汽车在跑")
class Bike:
def move(self):
print("自行车在骑")
class Bird:
def move(self):
print("鸟在飞")
def go(vehicle):
vehicle.move()
go(Car())
go(Bike())
go(Bird())继承下的多态
需要统一接口时,可在父类中声明方法,子类各自实现。父类中用 NotImplementedError 占位,可提示子类必须提供实现:
class Shape:
def area(self):
raise NotImplementedError("子类必须实现 area")
class Circle(Shape):
def __init__(self, r):
self.r = r
def area(self):
return 3.14 * self.r * self.r
class Square(Shape):
def __init__(self, side):
self.side = side
def area(self):
return self.side * self.side
shapes = [Circle(2), Square(3)]
for s in shapes:
print(s.area())内置函数中的多态
len 可作用于字符串、列表、元组等,因其均实现 __len__。自定义类提供同名特殊方法后,亦可接入 len:
print(len("hello"))
print(len([1, 2, 3]))
print(len((1, 2)))
class Team:
def __init__(self, members):
self.members = members
def __len__(self):
return len(self.members)
t = Team(["a", "b", "c"])
print(len(t))按类型分支:isinstance
多数情况下直接调用方法即可。若须按类型分别处理,可使用 isinstance。分支过多时,宜考虑是否可通过统一方法名消化差异:
def describe(x):
if isinstance(x, int):
return f"整数 {x}"
elif isinstance(x, str):
return f"字符串 '{x}'"
else:
return f"其他 {type(x).__name__}"
print(describe(5))
print(describe("hi"))常见错误
- 多态不依赖继承;鸭子类型下,具备同名方法即可被同一调用方使用。
- 传入缺少所需方法的对象将引发
AttributeError;边界输入复杂时,可先hasattr检查或由调用方保证接口。 - 父类提供空实现且未提示时,子类未重写可能静默出错;须强制子类实现时,宜使用
NotImplementedError。
本节说明了 Python 中多态与鸭子类型的含义。下一节介绍封装:命名约定、@property 与访问控制。