Python 的鸭子类型

先看一段很常见的"防御性"代码:

python 复制代码
def greet_all(names):
    if not isinstance(names, list):   # 先确认它是 list
        raise TypeError("必须是列表")
    for n in names:
        print("你好,", n)

很多从静态语言转过来的人,第一反应就是先检查类型:"传进来的必须是 list,不然不放心。"

可 Python 老手看到这段会皱眉:你凭什么非得让它是个 list? 只要它能被 for 遍历,tuple、set、甚至一个自定义对象,凭什么不能传?

这就是 Python 里最反直觉、也最舒服的一条哲学------鸭子类型(Duck Typing)。

"鸭子类型"这个名字,本身就是个比喻

它来自一句俏皮话:

如果它走起来像鸭子,叫起来像鸭子,那它就是鸭子。

翻译成代码语言就是:别关心对象"是什么",只关心它"能做什么" 。一个对象只要能调用 quack(),那不管它是真鸭子、橡皮鸭子还是机器人鸭子,对我来说就是鸭子。

静态语言的"证件" vs 动态语言的"本事"

在 Java 这类静态语言里,一个方法要收什么,得写在脸上:

java 复制代码
void makeDuckSound(Duck duck) {   // 参数类型锁死:必须是 Duck
    duck.quack();
}

你想传个"会叫的狗"进去?对不起,它没继承 Duck,编译都过不了。想让它被接受,得先让它实现某个接口或继承某个类------相当于先办张"鸭子证"。

Python 反过来了:

python 复制代码
def make_duck_sound(anything):
    anything.quack()    # 不查户口,直接看它会不会 quack

传进来的是啥我不管,你只要会 quack(),我就敢用你 。一个类只要定义了 quack 方法,它就是"鸭子",哪怕它名字叫 Dog。

好处坏处,都藏在这份"不设防"里

好处是灵活得让人上瘾 。写一个通用函数,不用为每一种类型写一份重载,也不用搞一堆抽象基类。很多 Python 的"魔法"都建立在鸭子类型上------比如 for 循环不关心你是什么,只要你实现了 __iter__;len() 不关心你是什么,只要你实现了 __len__;with 不关心你是什么,只要你实现了 __enter__ 和 __exit__(写了三十行 try...finally,Python 一个 with 就替你兜底了------别再用完才想起关文件)。

坏处是"不报错"不等于"对" 。静态语言在编译期就把"类型不对"拦下来了;鸭子类型则把检查推迟到了运行期------你传错对象,只有跑到那一行、调用那个不存在的方法时,才 AttributeError 崩掉。

所以 Python 社区演化出了两句心法:

  • LBYL(Look Before You Leap) :跳之前先看一眼------if isinstance(...),就是开头那种"防御式";
  • EAFP(Easier to Ask Forgiveness than Permission) :与其请求许可,不如事后道歉------先直接干,出错了再 except 兜着。

Python 官方偏爱的,是后者。看这段:

python 复制代码
def get_user_agent(request):
    try:
        return request.user_agent    # 直接访问,赌它有这个属性
    except AttributeError:
        return "unknown"

不检查"你是不是有 user_agent",直接取,取不到就默认。这就是鸭子类型的日常。

一份自由,配一份责任

鸭子类型给的是自由 :你可以不办任何"证件",就写出极其通用、极其简洁的代码。但它同时也把责任悄悄推给了你:因为编译器不帮你把关了,你得自己心里清楚"我期望这个对象会什么",并把边界(或异常)处理干净。

说到底,鸭子类型信的是这么一句话:能力比出身重要,行为比标签重要。

一个对象配不配得上某个角色,不该看它"登记"成了什么,而该看它"实际"能做什么。写代码是这个理,看人,不也常常是这个理么。

想系统掌握 Python 这些动态特性背后的机制,推荐 B站【408实验室】的《Python 完全自学教程》。

相关推荐
ThinkerQAQ_1 小时前
并发编程(七):volatile——从语言规则到 CPU
java·python·go
kakakahahahaha1 小时前
电脑显示器无信号排查:HDMI/DP接口、独立显卡、内存与安全模式
windows·安全·电脑·笔记本电脑·内容运营·软件需求
DanCheng-studio1 小时前
毕业设计项目 基于深度学习的图像修复算法 DCGAN
python·毕业设计·毕设
1024奇点1 小时前
【仓颉语言入门 · 第24课】
开发语言·ide·开源
栈溢出了1 小时前
VS Code Python 自动导包失效排查笔记
开发语言·笔记·python
DanCheng-studio1 小时前
毕业设计项目 基于深度学习的抽烟行为检测算法实现(源码分享)
python·毕业设计·毕设
朝朝辞暮i1 小时前
C++ 第 28 课:Lambda 表达式
开发语言·c++·算法
Frank_refuel1 小时前
深入理解RpcRouter(远端调用模块)
开发语言·qt
Wx-bishekaifayuan1 小时前
springboot陨石鉴收系统96265-计算机课程设计、毕业设计
java·vue.js·spring boot·后端·python·spring·课程设计