Python 中的多态,不止是“重写”——聊聊鸭子类型与动态协议
一、传统多态 vs Python 多态
在编译型语言(如 Java)中,多态通常通过继承 + 方法重写(override)和方法重载(overload)来实现。编译器会在编译期检查方法签名,确保子类正确覆写了父类方法,或者同一个类中有多个同名但参数不同的方法。
但在 Python 中,没有方法重载(同名方法只能有一个),也不强制子类重写父类方法。那么 Python 的多态体现在哪里?
其实,Python 更关注对象的行为,而不是对象的类型。只要一个对象拥有某个方法,我们就可以调用它——这就是鸭子类型(Duck Typing):“如果它走起来像鸭子,叫起来像鸭子,那么它就是鸭子。”
二、代码实战:一个函数,多种飞翔
class Bird: def fly(self): print("鸟在飞") class Airplane: def fly(self): print("飞机在飞") class Dog: def fly(self): print("狗在飞(哈哈,被抛起来的)") class Cat: pass # 没有 fly 方法 def start_flying(obj): obj.fly() # 只要对象有 fly() 即可,不关心类型 start_flying(Bird()) # 鸟在飞 start_flying(Airplane()) # 飞机在飞 start_flying(Dog()) # 狗在飞 # start_flying(Cat()) # 报错 AttributeError,因为 Cat 没有 fly三、专业术语解析
运行时多态(Runtime Polymorphism):方法调用在运行时才确定具体调用哪个实现,Python 天生如此。
协议(Protocol):在 Python 中,协议是一种非正式接口,例如“可迭代协议”要求实现
__iter__,“上下文协议”要求实现__enter__和__exit__。fly方法也是一种自定义协议。动态类型(Dynamic Typing):变量无需声明类型,解释器在运行时检查对象是否有对应属性。
EAFP(Easier to Ask for Forgiveness than Permission):Python 编程风格——直接尝试调用,如果出错再捕获异常,而不是事先用
isinstance检查类型。
四、与多态相关的代码补充
在提供的86.多态.py中,还演示了在同一个类内通过*args模拟方法重载,以及子类重写父类方法的经典方式:
class Person: def get_info(self, *args): # 可变参数实现“多参”效果 for p in args: print(p) class SupperPerson(Person): def get_info(self): # 重写父类方法 return self.__str__()
这种方式虽然不像真正的重载那样严谨,但足够灵活,满足业务需求。
五、总结
Python 的多态不依赖继承体系,而是依赖对象是否实现了所需的方法。
鸭子类型让代码更简洁、更通用,但也需要开发者对“协议”有清晰认知。
编写函数时,尽量接受“任意对象”,只要求它具备某些方法,这样代码复用性更高。
下篇预告:如何强制子类实现某个方法?抽象基类(ABC)来帮忙。
