Duck-typing Programming
目录
翻译自Duck Typing
概念
Duck Typing是一种编程方式,其中传递给函数或方法的对象,期望在运行时,该对象满足函数或方法所需对象的所有方法签名和属性。 即只关注行为,而不关注类型。
对象的类型本身并不重要。相反,该对象应该支持所有函数或方法在其上调用的所有方法签名/属性。
出于这个原因,Duck typing 有时被视为“一种思考方式而不是一种类型系统”。
在Duck typing中,我们不在函数原型或方法中声明参数类型。 这意味着编译器无法进行类型检查。
真正重要的是这种编程方式认为对象在运行时具有特定的方法/属性。
因此,动态语言通常支持Duck typing。 然而,一些静态语言开始通过结构类型“模仿”这种编程范式。
SAMPLE CODE
1 | # Source: Wikipedia, 2017. |
讨论
名字由来
如果它像鸭子一样走路,像鸭子一样游泳,像鸭子一样嘎嘎叫,那么它就是一只鸭子。
If it walks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck.
即像鸭子一样的非鸭子实体也可以被视为鸭子,因为重点在于行为。
因此通过类比,只要对象的行为符合预期,对象的类型就不重要了。
例如,Book类具有属性numPages和方法getPage(number),其中number是整数。假设函数searchPhrase(book, phrase)用于搜索书中的给定短语。这个函数会调用给定book中的numPages和getPage()。
Newspaper显然不是Book,但是对于Duck typing这不重要。如果Newspaper已经实现了numPages和getPage(number),那么它就可以传递给searchPhrase(book, phrase)。
Duck-typing 优势
便捷 > 类型安全
Duck-typing 不是类型系统,它为程序员提供了巨大的灵活性,常见于动态类型语言。 例如,在Python中,许多地方都容易编码。
一般而言,动态类型(dynamic typing)通常会缩短开发时间。 更少的冗余而又不得不写的代码,并且代码也更容易理解。 使用静态类型,编译时的类型检查降低了开发速度,但是在之后使用静态类型可以获得更好的性能。 因此有些人推荐建议Duck-typing仅用于原型设计,而不用于生产。
虽然编译时检查对于尽早发现潜在问题很有用,不过Duck-typing也可以通过强制执行编码约定,文档和测试驱动方法来确保代码的健壮。
Duck typing 和 Late binding
Late binding 是指在运行时将对象或方法绑定到指定的名字上。Duck typing看起来类似于Late binding,但是有一个微妙的差别,Duck typing基于行为而不是声明的类型。
Duck typing 和 Structural typing
Duck typing 可以分为静态Duck typing和动态Duck typing,对于后者,跳过编译时检查,这就是一般提及Duck typing时的含义。
静态Duck typing和一些静态类型语言(Scala,F#)支持的Structural typing很相似。 Structural typing允许进行一些编译时检查,注意检测的不是类型,而是支持的方法/属性。 Structural typing可以看作是由编译器保证的Duck typing的子集。 但是,我们还应该注意Structural typing基本上是静态类型,而Duck typing是动态的。
Duck typing 和 implied interfaces
静态类型语言使用显式接口。 在C#中,可以使用interface:任何实现interface的类型都可以传递给接受该接口的方法/函数。 这就是如何将灵活性构建到静态类型语言中,但这需要接口声明。
Python则没有显式接口:隐含接口。 对于Python,隐式接口与Duck typing没有什么不同。
但是,对于C++,在其STL库中支持隐含接口。 鉴于C++进行编译时检查,我们通常不能说隐含的接口与Duck typing相同。 请注意,当接口为隐式时,没有正式的接口定义,一般而言只有接口文档。
Duck typing 与 LBYL, EAFP
LBYL: Look Before You Leap
EAFP: Easy to Ask for Forgiveness than Permission
这是两个相反的概念,都可以用Python来讨论,虽然Python更喜欢EAFP方法。
对于LBYL,我们通常在对象执行操作之前检查对象是否是可兼容类型,例如调用其方法。Python有一个内置函数isinstance()来做到这一点。
对于EAFP,我们只需调用对象的方法,期望该方法存在。如果不存在,则Python解释器将抛出异常,代码应该处理该异常。
在Python的Duck typing中,验证对象的类型的行为很不Pythonic。 因此,尽管可以使用hasattr()来验证对象是否含有某个属性,但Python更喜欢EAFP,这也正好和Duck typing的思想一致。
Reference
DEVOPEDIA Duck Typing