Python鸭子多态
在 Python 中,“鸭子多态”通常指的是通过鸭子类型实现的多态行为。它不是一种独立的技术,而是 Python 动态类型特性带来的编程风格。
核心概念:鸭子类型
“如果它走路像鸭子,叫起来像鸭子,那么它就是鸭子。”
在 Python 中,一个对象的适用性取决于它是否具有所需的方法和属性,而不是它的实际类型。不需要显式继承同一个父类或实现同一个接口。
与静态语言多态的区别
| 静态语言(如 Java/C++) | Python(鸭子类型) |
|---|---|
| 多态依赖继承或接口实现 | 多态依赖对象行为(方法存在即可) |
| 必须显式声明类型关系 | 运行时动态检查方法 |
| 类型检查在编译期完成 | 类型检查在运行时进行(AttributeError) |
简单示例
classDuck:defquack(self):print("嘎嘎嘎")classPerson:defquack(self):print("我在模仿鸭子叫:嘎嘎嘎")classDog:defbark(self):print("汪汪汪")defmake_it_quack(thing):"""不关心 thing 是什么类型,只关心它能不能 quack"""thing.quack()# 使用duck=Duck()person=Person()dog=Dog()make_it_quack(duck)# 输出:嘎嘎嘎make_it_quack(person)# 输出:我在模仿鸭子叫:嘎嘎嘎# make_it_quack(dog) # 报错 AttributeError,因为 Dog 没有 quack 方法优点与注意事项
优点:
- 代码极其灵活,减少样板代码(无需为了复用而强行继承)
- 鼓励编写基于“协议”(如
__iter__、__len__)的通用代码
注意事项(也是缺点):
- 运行时错误风险:如果传入的对象没有所需方法,会在运行时抛出
AttributeError - 可读性降低:需要依靠文档或注释说明函数期望的“隐式接口”
常见的“鸭子协议”举例
Python 内置的很多功能都依赖鸭子类型,例如:
- 可迭代对象:只要定义了
__iter__()或__getitem__(),for循环就能处理它。 - 上下文管理器:只要定义了
__enter__()和__exit__(),就能用with语句。 - 文件类对象:只要实现了
read()/write(),就能传给json.load()等函数。
这种“行为决定身份”的思想,正是 Python 多态的精髓。
