鸭子类型 Duck typing
在看ruby的时候,发现ruby有一种 duck typing 编程风格,这属于语言思想,科普一下
在程序设计中,鸭子类型(英语:duck typing)是动态类型的一种风格。在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由当前方法和属性的集合决定。这个概念的名字来源于由James Whitcomb Riley提出的鸭子测试(见下面的“历史”章节),“鸭子测试”可以这样表述:
- “当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”[1][2]
在鸭子类型中,关注的不是对象的类型本身,而是它是如何使用的。例如,在不使用鸭子类型的语言中,我们可以编写一个函数,它接受一个类型为鸭的对象,并调用它的走和叫方法。在使用鸭子类型的语言中,这样的一个函数可以接受一个任意类型的对象,并调用它的走和叫方法。如果这些需要被调用的方法不存在,那么将引发一个运行时错误。任何拥有这样的正确的走和叫方法的对象都可被函数接受的这种行为引出了以上表述,这种决定类型的方式因此得名。
鸭子类型通常得益于不测试方法和函数中参数的类型,而是依赖文档、清晰的代码和测试来确保正确使用。从静态类型语言转向动态类型语言的用户通常试图添加一些静态的(在运行之前的)类型检查,从而影响了鸭子类型的益处和可伸缩性,并约束了语言的动态特性。
目录[隐藏]
|
[编辑]概念样例
考虑用于一个使用鸭子类型的语言的以下伪代码:
function calculate(a, b, c) => return (a+b)*cexample1 = calculate (1, 2, 3)
example2 = calculate ([1, 2, 3], [4, 5, 6], 2)
example3 = calculate ('apples ', 'and oranges, ', 3)print to_string example1
print to_string example2
print to_string example3
在样例中,每次对calculate
的调用都使用的对象(数字、列表和字符串)在继承关系中没有联系。只要对象支持“+”和“*”方法,操作就能成功。例如,翻译成Ruby或Python语言,运行结果应该是:
9
[1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6]
apples and oranges, apples and oranges, apples and oranges,
这样,鸭子类型在不使用继承的情况下使用了多态。唯一的要求是calculate
函数需要作为参数的对象拥有“+”和“*”方法。以下样例(python语言)体现了鸭子测试。就in_the_forest
函数而言,对象是一个鸭子:
class Duck:def quack(self): print "Quaaaaaack!"def feathers(self): print "The duck has white and gray feathers."class Person:def quack(self):print "The person imitates a duck."def feathers(self): print "The person takes a feather from the ground and shows it."def in_the_forest(duck):duck.quack()duck.feathers()def game():donald = Duck()john = Person()in_the_forest(donald)in_the_forest(john)game()
[编辑]静态语言中的鸭子类型
一些通常的静态,语言如Boo和C#第四版,有一些额外的类型注解,它们指示编译器将类的类型检查安排在运行时而不是编译时,并在编译器的输出中包含用于运行时类型检查的代码[3][4]。这些附加的内容允许这些语言享受鸭子类型的大多数益处,仅有的缺点是需要在编译时识别和指定这些动态类。
[编辑]与其他类型系统的比较
[编辑]结构类型系统
鸭子类型和结构类型相似但与之不同。结构类型由类型的结构决定类型的兼容性和等价性,而鸭子类型只由结构中在运行时所访问的部分决定类型的兼容性。Objective Caml语言使用结构类型系统。
[编辑]接口
接口可以提供鸭子类型的一些益处,但鸭子类型与之不同的是没有显式定义任何接口。例如,如果一个第三方Java库实现了一个用户不允许修改的类,用户就无法把这个类的实例用作一个自己定义的接口的实现,而鸭子类型允许这样做。
[编辑]模板或泛型
模板函数或方法在一个静态类型上下文中应用鸭子测试;这同时带来了静态和动态类型检查的一般优点和缺点。同时,由于在鸭子类型中,只有在运行时被实际调用的方法需要被实现,而模板要求实现在编译时不能证明不可到达的所有方法,因此鸭子类型更具有可伸缩性。
实例包括带有模板的C++语言和Java语言的泛型。
[编辑]批评
常常被引用的一个批评是:
- 关于鸭子类型的一个问题是它要求程序员在任何时候都必须很好地理解他/她正在编写的代码。在一个强静态类型的、使用了类型继承树和参数类型检查的语言中,给一个类提供未预测的对象类型更为困难。例如,在Python中,你可以创建一个称为Wine的类,并在其中需要实现press方法。然而,一个称为Trousers的类可能也实现press()方法。为了避免奇怪的、难以检测的错误,开发者在使用鸭子类型时需要意识到每一个“press”方法的可能使用,即使在语义上和他/她所正在编写工作的代码没有任何关系。
- 本质上,问题是:“如果它走起来像鸭子并且叫起来像鸭子”,它也可以是一只正在模仿鸭子的龙。尽管它们可以模仿鸭子,但也许你不总是想让龙进入池塘。
鸭子类型的提倡者,如Guido van Rossum,认为这个问题可以通过在测试和维护代码库前拥有足够的了解来解决[5][6]。
对鸭子类型的批评倾向于成为关于动态类型和静态类型的争论的更广阔的观点的特殊情形。
[编辑]历史
Alex Martelli很早(2000年)就在发布到comp.lang.python新闻组上的一则消息中使用了这一术语。他同时对鸭子测试的错误的字面理解提出了提醒,以避免人们错误认为这个术语已经被使用。
- 换言之,不要检查它是不是一个鸭子:检查它像不像一个鸭子地叫,等等。取决于你需要哪个像鸭子的行为的子集来使用语言。
[编辑]实现
[编辑]在ColdFusion中
web应用程序脚本语言ColdFusion允许函数参数被指定为类型为any。对于这种参数,任意对象都可被传入,函数调用在运行时被动态绑定。如果对象没有实现一个被调用的函数,一个可被捕获并优雅地处理的运行时例外将被抛出。在ColdFusion 8中,这也可以被一个已定义的事件onMissingMethod()而不是例外处理器处理。另一个可替代的参数类型WEB-INF.cftags.component限制传入参数是一个ColdFusion组件(CFC),在一个不正确的对象传入时它提供了更好的错误消息。
[编辑]在Objective-C中
Objective-C,C和Smalltalk的一个交错,像Smalltalk一样,允许用户声明一个对象的类型为“id”并向它发送任何信息。发送者可以测试一个对象以了解它能不能对一个消息响应,对象可以在收到消息的时候决定响应与否,如果发送者发送了一个接收者不能响应的消息,一个例外会被抛出。因此,鸭子类型在Objective-C中被完全支持。
[编辑]在Python中
鸭子类型在Python中被广泛使用。Python术语表这样定义鸭子类型:
Pythonic programming style that determines an object's type by inspection of its method or attribute signature rather than by explicit relationship to some type object ("If it looks like a duck and quacks like a duck, it must be a duck.") By emphasizing interfaces rather than specific types, well-designed code improves its flexibility by allowing polymorphic substitution. Duck-typing avoids tests using
type()
orisinstance()
. Instead, it typically employs the EAFP (Easier to Ask Forgiveness than Permission) style of programming.
在Python中,鸭子类型的最典型例子就是类似file的类。这些类可以实现file
的一些或全部方法,并可以用于file
通常使用的地方。例如,GzipFile
实现了一个用于访问gzip压缩的数据的类似file的对象。cStringIO
允许把一个Python字符串视作一个文件。套接字(socket)也和文件共同拥有许多相同的方法。然而套接字缺少tell()
方法,不能用于GzipFile
可以使用的所有地方。这体现了鸭子类型的可伸缩性:一个类似file的对象可以实现它有能力实现的方法,且只能被用于它有意义的情形下。
EAFP原则描述了例外处理的使用。例如相对于检查一个自称为类似Duck的对象是否拥有一个quack()方法(使用if hasattr(mallard, "quack"): ...
),人们通常更倾向于用例外处理把对quack的调用尝试包裹起来:
try:mallard.quack() except (AttributeError, TypeError):print "mallard can't quack()"
这个写法的优势在于它鼓励结构化处理其他来自类的错误(这样的话,例如,一个不能完成quack的Duck子类可以抛出一个“QuackException”,这个例外可以简单地添加到包裹它的代码,并不需要影响更多的代码的逻辑。同时,对于其他不同类的对象存在不兼容的成员而造成的命名冲突,它也能够处理(例如,假设有一个医学专家Mallard有一个布尔属性将他分类为“quack=True”,试图执行Mallard.quack()将抛出一个TypeError)。
在更实际的实现类似file的行为的例子中,人们更倾向于使用Python的异常处理机制来处理各种各样的可能因为各种程序员无法控制的环境和operating system问题而发生的I/O错误。在这里,“鸭子类型”产生的例外可以在它们自己的子句中捕获,与操作系统、I/O和其他可能的错误分别处理,从而避开复杂的检测和错误检查逻辑。
[编辑]在Common Lisp中
Common Lisp提供了一个面向对象的扩展(Common Lisp对象系统,简写为CLOS)。在Common Lisp中,CLOS和Lisp的动态类型使鸭子类型成为一种通用的编程风格。
使用Common Lisp,用户通常不需要查询类型,因为如果一个函数不适用,系统会抛出一个运行时错误。这个错误可以被Common Lisp的条件系统处理。在类外定义的方法也可以为指定的对象定义。
(defclass duck () ())(defmethod quack ((a-duck duck))(print "Quaaaaaack!"))(defmethod feathers ((a-duck duck))(print "The duck has white and gray feathers."))(defclass person () ())(defmethod quack ((a-person person))(print "The person imitates a duck."))(defmethod feathers ((a-person person))(print "The person takes a feather from the ground and shows it."))(defmethod in-the-forest (duck)(quack duck)(feathers duck))(defmethod game ()(let ((donald (make-instance 'duck))(john (make-instance 'person)))(in-the-forest donald)(in-the-forest john)))(game)
Common Lisp通常的开发风格(像SLIME一样使用Lisp REPL)也允许交互式修复:
? (defclass cat () ()) #<STANDARD-CLASS CAT> ? (quack (make-instance 'cat)) > Error: There is no applicable method for the generic function: > #<STANDARD-GENERIC-FUNCTION QUACK #x300041C2371F> > when called with arguments: > (#<CAT #x300041C7EEFD>) > If continued: Try calling it again 1 > (defmethod quack ((a-cat cat))(print "The cat imitates a duck."))#<STANDARD-METHOD QUACK (CAT)> 1 > (continue)"The cat imitates a duck."
通过这种方法,软件可以通过扩展只有部分工作的使用鸭子类型的代码来开发。
鸭子类型 Duck typing相关推荐
- Python编程基础:第四十九节 鸭子类型Duck Typing
第四十九节 鸭子类型Duck Typing 前言 实践 前言 本节我们一起学习一个非常有趣的知识点:鸭子类型.有这么一句话:If it walks like a duck, and it quacks ...
- duck typing java_编程语言中的鸭子类型 Duck Typing
来源 | https://segmentfault.com/a/1190000019607240 1.什么是鸭子类型(duck typing) 百度百科是这样解释的: 这是程序设计中的一种类型推断风格 ...
- 编程语言中的鸭子类型 Duck Typing
来源 | https://segmentfault.com/a/1190000019607240 1.什么是鸭子类型(duck typing) 百度百科是这样解释的: 这是程序设计中的一种类型推断风格 ...
- 【Python】浅谈 鸭子类型 (Duck Typing)
目录 一.来源 二.说明 三.举例 四.不足 一.来源 在程序设计中,鸭子类型 (duck typing) 是动态类型的一种风格.在此风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口, ...
- 鸭子类型duck typing(动态)
在程序设计中,鸭子类型(duck typing)是动态类型的一种风格.在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由当前方法和属性的集合决定.这个概念的名字来源于由Ja ...
- Python笔记 · 鸭子类型 / Duck Typing
1. 问题的由来 我初次意识到鸭子类型的存在是在学习Sklearn时,在<Hands-On Machine Learing>一书的第二章,作者提供了一个自定义的Tansformer,使用自 ...
- [转]编程语言中的 鸭子模型(duck typing)
在学习Python的时候发现了鸭子类型这个术语,听起来好像很有意思一样,所以把它记下来. 鸭子类型的核心概念在于一个对象的有效语义,不是继承自特定的类或者实现特定的方法,而是 由当前的属性和方法集合决 ...
- python有多态特性吗_Python:多态、协议和鸭子类型
多态 问起面向对象的三大特性,几乎每个人都能对答如流:封装.继承.多态.今天我们就要来说一说 Python 中的多态. 所谓多态:就是指一个类实例的相同方法在不同情形有不同表现形式.多态机制使具有不同 ...
- python与鸭子类型
目录 1.面向对象(OOP)的三大特征 2.静态类型语言和动态类型语言的区别 3.面向接口编程 4.鸭子类型 5.python中的多态 1.面向对象(OOP)的三大特征 (1)面向对象程序设计有三大特 ...
- python鸭制作类代码_Python实现多态、协议和鸭子类型的代码详解
多态 问起面向对象的三大特性,几乎每个人都能对答如流:封装.继承.多态.今天我们就要来说一说 Python 中的多态. 所谓多态:就是指一个类实例的相同方法在不同情形有不同表现形式.多态机制使具有不同 ...
最新文章
- 【学术】直博和读完硕士再读博,在能力上的差距有多大?
- 压缩视频 html5播放,将HTML5视频呈现为Canvas正在压缩图像
- 【python】 web开发入门
- ASM_PREFERRED_READ_FAILURE_GROUPS
- Python小游戏(打地鼠)
- python变量贡献率排序_3.2.5 贡献度分析
- CentOS7下分布式文件系统FastDFS的安装 配置 (单节点)
- CodeForces - 233A Perfect Permutation
- 区间DP--凸多边形三角剖分
- 超越村后端开发(7:修改完善代码(持续更新))
- oracle decode函数
- 如果光猫+hadoop,有化学反应吗?
- 第七章产品生命周期管理
- Java多线程篇--线程的等待通知
- 家居行业渠道商销售系统线上线下一体化运作,促进产品更新迭代
- python进阶路线知乎_Python学习教程(Python学习路线):第九天-面向对象进阶
- macbook上好用的解压软件_好用的Mac解压软件推荐
- Matlab 2019a
- 【同等学力申硕】在职研究生? 有用吗?
- swoole学习(十二) - Hprose相关知识