鸭子类型是Python实现多态最自然的方式,只关注对象是否有所需方法而非其类型;运行时动态调用,零开销、兼容性强,但需靠文档和测试保障方法签名与返回值一致性。
是 Python 实现多态最自然、最常用的方式——它不依赖继承或接口声明,只关心“对象有没有那个方法”,而不是“它是不是某个类的实例”。
为什么直接用
就能实现多态
Python 在调用方法时完全不检查对象类型,只在运行时尝试访问属性或方法。只要对象有
方法,你就能传给
函数;只要它有
,
就能跑通。这种“行为即类型”的逻辑,就是鸭子类型的核心。
常见错误现象:写了个函数,加了类型提示如
,结果运行时报
——这是强行套 Java 思维,反而卡住自己
使用场景:快速原型、脚本工具、配置驱动逻辑(比如插件系统里加载任意导出器)
性能影响:零额外开销,比 ABC 或 Protocol 更轻量
兼容性:Python 2.7 起就支持,无版本限制
下怎么避免 AttributeError
不是所有“看起来像鸭子”的对象都真能叫出声。调用前没做兜底,容易在运行时崩。
用
提前判断,但略啰嗦
更推荐用
包裹调用,符合 Python 的 EAFP(Easier to Ask for Forgiveness than Permission)原则
如果是公共 API,建议在文档里明确写出“需实现
方法”,而不是靠类型注解约束
别在函数开头写
+
——这等于自己造了个弱版 ABC,又没 ABC 的 IDE 支持和强制力
和
、
的关键区别在哪
三者都能达成“同一函数处理多种类型”的效果,但设计意图和适用阶段不同:
Python 3.14.3
微软官方的 Python 扩展,是 VS Code 安装量最高的扩展(209M+)。集成 IntelliSense(通过 Pylance)、调试(通过 Python Debugger)、代码检查、格式化、重构和单元测试等功能。支持 Jupyter Notebook、虚拟环境管理和多 Python 版本切换。
下载
立即学习
“
Python免费学习笔记(深入)
”;
:写脚本、内部工具、快速迭代时首选。不写任何协议定义,靠约定和测试兜底
(Python 3.8+):需要静态类型检查(如 mypy)配合时用。IDE 能提示缺失方法,但运行时不强制
:团队协作、库开发、强契约场景。子类忘记实现
会在实例化时报错,不可绕过
注意:
这个
类型本身不能被实例化,也不参与运行时逻辑——它只活在类型检查阶段。
容易被忽略的边界点
看似自由,但真正落地时最容易栽在两个地方:一是方法签名不一致(比如一个
,另一个是
),二是返回值语义混乱(比如有的
返回路径字符串,有的返回布尔成功状态)。这些不会触发运行时错误,却会让上层逻辑悄悄出错。解决办法不是加类型系统,而是写清楚文档 + 补充单元测试覆盖不同实现。
duck typingduck typingexportexport_dataquackmake_it_quackdef process(obj: Exporter)TypeError: Exporter is not a concrete classclass PDFExporter:
def export(self, data):
print(f"PDF: {data}")
class CSVExporter:
def export(self, data):
print(f"CSV: {data}")
def export_data(exporter, data):
exporter.export(data) # 不检查 exporter 是什么类,只看有没有 export
export_data(PDFExporter(), "report")
export_data(CSVExporter(), "sales")
duck typinghasattr(obj, "export")try/except AttributeErrorexport(self, data)if not hasattr(...)raise TypeErrorProtocolABCduck typingProtocolABC@abstractmethodfrom typing import Protocol
class Exporter(Protocol):
def export(self, data: str) -> None: ...
def export_data(exporter: Exporter, data: str) -> None:
exporter.export(data) # mypy 会检查传入对象是否满足 Exporter 协议
ExporterProtocolduck typingexport(self, data)export(self, data, format="pdf")export