理解鸭子类型
一次被人问到鸭子类型,还不了解这个概念,今天再次偶遇这个概念,了解了一下之后发现原来这个概念在 python 编程中早已经在用到了,现在才明白这种用法就是『鸭子』。
维基百科里讲:
当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。
鸭子类型是动态语言里面的一种概念,简单来讲:不去关心这个对象是什么,而是关心这个对象能够做什么。比如说一个射击游戏中有一个函数能接受一个武器对象,这个对象可以进行『击发』和『装填』,那么任何可以进行击发和装填的对象都可以被这个函数没有错误地调用,他并不去关心这个对象到底是什么。因为对象里面有了『击发』和『装填』两个方法,他就认为这是一种武器。
讲到这里,我们很容易会想到静态类型的语言中的接口,只要实现了武器接口(其中有装填和击发两个方法)的对象也都可以被那个函数调用。这是和动态类型语言的鸭子模型相似的地方也是主要的区别所在——动态语言不用定义接口。
鸭子类型有被人批评的缺点:
本质上,问题是:“如果它走起来像鸭子并且叫起来像鸭子”,它也可以是一只正在模仿鸭子的龙。尽管它们可以模仿鸭子,但也许你不总是想让龙进入池塘。
比如,一个核弹控制器对象有 press_key 方法,一个音乐播放器也有 press_key 方法,如果不知道对象的 press_key 方法都会干些什么就丢给会调用参数 prees_key 方法的函数,可能会引起灾难性后果。
所以要正确使用鸭子类型,应如下所说:
鸭子类型通常得益于不测试方法和函数中参数的类型,而是依赖文档、清晰的代码和测试来确保正确使用。
ps:以前『无意中』用到鸭子类型的东西是我写的一个基于 Bootstrap 的表单生成工具,可以给 tornado 的 UImodule 使用。它有多个对应表单中不同类型的元素的处理对象,都实现了 action 方法,这样做到了便于拓展。
pps:鸭子类型并不只是 python,JavaScript 等脚本语言的福利,而是动态类型的语言,比如我最近在看的 Objective-C 语言有 id 类型,Go 语言有 interface{} 类型,都可以实现鸭子类型。
歪下楼顺便说动态语言 python 的『接口』。
动态语言模拟接口简单来说定义一个类,写几个实例方法,并在这些实例方法引发一个异常。然后要实现这个接口的类都去继承这个类,如果子类不去重写父类的这些方法,就会引发异常,这就相当于定义了接口。例如下面 python 代码:
::::python
class Weapon():
def fire(self):
raise Exception("Must implement interface method!")
class Gun(Weapon):
def repair(self):
// repair the gun
上面代码就会引发异常,因为 Gun 显式定义了要去实现 Weapon 接口,但是没有实现 fire 方法,所以会引发异常。

浙公网安备 33010602011771号