什么是鸭子类型?
一个对象有效的语义,
不是由继承自特定的类或实现特定的接口,
而是由"当前方法和属性的集合"决定。
这个概念的名字来源于由James Whitcomb Riley提出的鸭子测试,“鸭子测试”可以这样表述:
当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。
在鸭子类型中,关注的不是对象的类型本身,而是它是如何使用的。
- 在不使用鸭子类型的语言中,我们可以编写一个函数,
它接受一个类型为"鸭子"的对象
,并调用它的"走"和"叫"方法。 - 在使用鸭子类型的语言中,
这样的一个函数可以接受一个任意类型的对象
,并调用它的"走"和"叫"方法。(如果这些需要被调用的方法不存在,那么将引发一个运行时错误。任何拥有这样的正确的"走"和"叫"方法的对象都可被函数接受的这种行为引出了以上表述,这种决定类型的方式因此得名。)
注意:
鸭子类型通常得益于"不"测试方法和函数中参数的类型,而是依赖文档、清晰的代码和测试来确保正确使用。
从静态类型语言转向动态类型语言的用户通常试图添加一些静态的(在运行之前的)类型检查,从而影响了鸭子类型的益处和可伸缩性,并约束了语言的动态特性。
在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() or isinstance(). 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的对象可以实现它有能力实现的方法,且只能被用于它有意义的情形下。
异常处理:
例如相对于检查一个自称为类似Duck的对象是否拥有一个quack()方法
if hasattr(mallard, "quack"): ...
人们通常更倾向于用异常处理把对quack的调用尝试包裹起来:
try:
mallard.quack()
except (AttributeError, TypeError):
print "mallard並沒有quack()函式"
这个写法的优势在于它鼓励结构化处理其他来自类的错误(这样的话,例如,一个不能完成quack的Duck子类可以抛出一个“QuackException”,这个异常可以简单地添加到包裹它的代码,并不需要影响更多的代码的逻辑。
在更实际的实现类似file的行为的例子中,人们更倾向于使用Python的异常处理机制来处理各种各样的可能因为各种程序员无法控制的环境和operating system问题而发生的I/O错误。在这里,“鸭子类型”产生的异常可以在它们自己的子句中捕获,与操作系统、I/O和其他可能的错误分别处理,从而避开复杂的检测和错误检查逻辑
批评
关于鸭子类型常常被引用的一个批评是:
它要求程序员在任何时候都必须很好地理解他/她正在编写的代码。
在一个强静态类型的、使用了类型继承树和参数类型检查的语言中,给一个类提供未预测的对象类型更为困难。
例如,在Python中,你可以创建一个称为Wine的类,并在其中需要实现press方法。然而,一个称为Trousers的类可能也实现press()方法。为了避免奇怪的、难以检测的错误,开发者在使用鸭子类型时需要意识到每一个“press”方法的可能使用,即使在语义上和他/她所正在编写工作的代码没有任何关系。
本质上,问题是:“如果它走起来像鸭子并且叫起来像鸭子”,它也可以是一只正在模仿鸭子的龙。尽管它们可以模仿鸭子,但也许你不总是想让龙进入池塘。
鸭子类型的提倡者,如吉多·范罗苏姆,认为这个问题可以通过在测试和维护代码库前拥有足够的了解来解决
对鸭子类型的批评倾向于成为关于动态类型和静态类型的争论的更广阔的观点的特殊情形。
强烈推荐
参考
https://zh.wikipedia.org/wiki/%E9%B8%AD%E5%AD%90%E7%B1%BB%E5%9E%8B#.E6.89.B9.E8.AF.84
网友评论