打开主菜单

求真百科

鸭子类型

鸭子类型(程序设计动态)原图链接来自 知乎 的图片

鸭子类型(英语:duck typing)是指在程序设计中动态类型的一种风格。在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由"当前方法和属性的集合"决定。[1]

目录

简介

在程序设计中,鸭子类型(英语:duck typing)是动态类型的一种风格。在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由"当前方法和属性的集合"决定。这个概念的名字来源于由James Whitcomb Riley提出的鸭子测试(见下面的“历史”章节),“鸭子测试”可以这样表述:

“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”

在鸭子类型中,关注点在于对象的行为,能作什么;而不是关注对象所属的类型。例如,在不使用鸭子类型的语言中,我们可以编写一个函数,它接受一个类型为"鸭子"的对象,并调用它的"走"和"叫"方法。在使用鸭子类型的语言中,这样的一个函数可以接受一个任意类型的对象,并调用它的"走"和"叫"方法。如果这些需要被调用的方法不存在,那么将引发一个运行时错误。任何拥有这样的正确的"走"和"叫"方法的对象都可被函数接受的这种行为引出了以上表述,这种决定类型的方式因此得名。

鸭子类型通常得益于"不"测试方法和函数中参数的类型,而是依赖文档、清晰的代码和测试来确保正确使用。

概念样例

考虑用于一个使用鸭子类型的语言的以下伪代码:在样例中,每次对calculate的调用都使用的对象(数字、列表和字符串)在继承关系中没有联系。只要对象支持“+”和“*”方法,操作就能成功。例如,翻译成Ruby或Python语言,运行结果应该是:

这样,鸭子类型在不使用继承的情况下使用了多态。唯一的要求是calculate函数需要作为参数的对象拥有“+”和“*”方法。以下样例(Python语言)体现了鸭子测试。就in_the_forest函数而言,对象是一个鸭子:

静态语言中的鸭子类型

一些通常的静态语言如Boo和C#第四版,有一些额外的类型注解,它们指示编译器将类的类型检查安排在运行时而不是编译时,并在编译器的输出中包含用于运行时类型检查的代码。这些附加的内容允许这些语言享受鸭子类型的大多数益处,仅有的缺点是需要在编译时识别和指定这些动态类。

鸭子类型和结构类型相似但与之不同。结构类型由类型的结构决定类型的兼容性和等价性,而鸭子类型只由结构中在运行时所访问的部分决定类型的兼容性。Objective Caml语言使用结构类型系统。

接口

接口可以提供鸭子类型的一些益处,但鸭子类型与之不同的是没有显式定义任何接口。例如,如果一个第三方Java库实现了一个用户不允许修改的类,用户就无法把这个类的实例用作一个自己定义的接口的实现,而鸭子类型允许这样做。

模板或泛型

模板函数或方法在一个静态类型上下文中应用鸭子测试;这同时带来了静态和动态类型检查的一般优点和缺点。同时,由于在鸭子类型中,只有“在运行时被实际调用的”方法需要被实现,而模板要求实现“在编译时不能证明不可到达的”所有方法,因此鸭子类型更具有可伸缩性。

实例包括带有模板的C++语言和Java语言的泛型。

批评

关于鸭子类型常常被引用的一个批评是它要求程序员在任何时候都必须很好地理解他/她正在编写的代码。在一个强静态类型的、使用了类型继承树和参数类型检查的语言中,给一个类提供未预测的对象类型更为困难。例如,在Python中,你可以创建一个称为Wine的类,并在其中需要实现press方法。然而,一个称为Trousers的类可能也实现press()方法。为了避免奇怪的、难以检测的错误,开发者在使用鸭子类型时需要意识到每一个“press”方法的可能使用,即使在语义上和他/她所正在编写工作的代码没有任何关系。

本质上,问题是:“如果它走起来像鸭子并且叫起来像鸭子”,它也可以是一只正在模仿鸭子的龙。尽管它们可以模仿鸭子,但也许你不总是想让龙进入池塘。鸭子类型的提倡者,如吉多·范罗苏姆,认为这个问题可以通过在测试和维护代码库前拥有足够的了解来解决。对鸭子类型的批评倾向于成为关于动态类型和静态类型的争论的更广阔的观点的特殊情形。

历史

Alex Martelli很早(2000年)就在发布到comp.lang.python新闻组上的一则消息中使用了这一术语。他同时对鸭子测试的错误的字面理解提出了提醒,以避免人们错误认为这个术语已经被使用。

“换言之,不要检查它是不是一个鸭子:检查它像不像一个鸭子地叫,等等。取决于你需要哪个像鸭子的行为的子集来使用语言。”

web应用程序脚本语言ColdFusion允许函数参数被指定为类型为any。对于这种参数,任意对象都可被传入,函数调用在运行时被动态绑定。如果对象没有实现一个被调用的函数,一个可被捕获并优雅地处理的运行时异常将被抛出。在ColdFusion 8中,这也可以被一个已定义的事件onMissingMethod()而不是异常处理器处理。另一个可替代的参数类型WEB-INF.cftags.component限制传入参数是一个ColdFusion组件(CFC),在一个不正确的对象传入时它提供了更好的错误消息。

参见

鸭子测试(duck test)

视频

鸭子类型 相关视频

Python vs Go 鸭子类型
《看漫画学Python》9.7.2 鸭子类型测试与多态!

参考文献

  1. 什么是鸭子类型(duck typing),博客园,2017-06-28