鸭子类型
前言
在学习 Python 的过程中,你一定会听说过这个名词:鸭子类型(duck typing)。关于鸭子类型最有名的一句话是:
“当你看到一只鸟走起来像鸭子、游起泳来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”
这句话来源于詹姆斯·惠特科姆·莱利提出的鸭子测试。现在许多 Python 的教科书上都会提到这个概念,但有些书上并没有展开介绍细节。
本文就通过一些实例来探究一下什么是鸭子类型。
本文中的命令行示例,都是基于以下环境:
- 操作系统: CentOS 7
- Python:3.7.9
- GCC:4.8.5
多态性
在理解鸭子类型之前,我们先要了解一下什么是多态性。概括地说,多态性(polymorphism)
表示为”相同的接口,不同的实现“,或”一个接口,多种方法“。
多态性一般又分为编译时多态性
和运行时多态性
。以 C++ 为例,编译时多态性
通过函数重载实现,在编译环节实现早期绑定(early binding)
;而运行时多态性
通过虚函数实现,在运行环节实现延迟绑定(late binding)
,也称为动态绑定(dynamic binding)
。
运行时多态性
,或者动态绑定
,就是指在父类中定义的属性和方法被子类继承之后,可以具有不同的数据类型或表现出不同的行为。它是面向对象编程的一个重要概念。本文后面提到的多态性,默认指运行时多态性。
以 C++ 为例:
|
|
执行结果:
|
|
可以看到,虽然函数 f 的形参是 Animal 类型的指针,但实际使用中,我们可以把任何 Animal 子类类型的指针传递给 f 函数。而在函数内部,当我们通过 Animal 指针来调用 shout() 方法的时候,实际调用的是具体对象所属类型的方法,而不是基类的方法。
编程语言中的类型系统 一文中本人介绍过,静态类型语言,或多或少也会做一些动态类型检查,一个典型的例子是C++ 运行时类型检查(C++ RTTI)
。C++ 动态类型检查是为了实现动态绑定,就像这个例子演示的那样,一个基类类型的指针或引用,运行时指向的对象可以是基类对象本身,也可以子类的对象,而通过该指针或引用来访问方法的时候,会根据实际指向的对象类型来调用对应的方法。
Python 中的多态性
上面介绍了在静态类型语言 C++ 中实现多态性,那么在 Python 中如何实现呢?
Python 中实现多态性,要比 C++ 简单,上一个例子,用 Python 实现如下;
|
|
执行结果:
|
|
Python 的实现比 C++ 简单,一方面因为 Python 语法简单,另一方面因为 Python 是动态类型语言,声明变量、形参等不需要指定类型,当调用一个对象的方法的时候,会根据对象的实际类型来调用对应的方法。
如果说 C++ 的多态性是靠 C++ RTTI 实现了动态绑定,Python 的多态性就完全是“天生”的,Python 的动态类型检查使得多态性在面向对象程序中体现得淋漓尽致。其实在 Python 中,即便不通过类的继承,也能实现动态绑定。这个方法就是鸭子类型。
什么是鸭子类型
鸭子类型(duck typing)
在程序设计中是动态类型的一种风格。回到关于鸭子类型的那句名言:当你看到一只鸟走起来像鸭子、游起泳来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。中翻中,这句话的意思就是到底是鸭子还是鸟,要看它的行为,而不是类型。换句话说,在鸭子类型中,关注点在于对象的行为,而不是对象所属的类型。
在不使用鸭子类型的语言中,我们编写一个函数,它只能接受一个类型为"鸭子"的对象,并调用它的“叫”方法。在使用鸭子类型的语言中,编写一个函数可以接受一个任意类型的对象,并调用它的“叫”方法。如果这个对象不支持“叫”方法,那么将引发一个运行时错误。任何拥有这样的“叫”方法的对象都可被函数接受,这种决定类型的方式因此得名。
我们把上一节的例子稍作改动:
|
|
执行结果:
|
|
我们新增了一个类 Cat,定义了它的 shout() 方法。这个类并没有继承自 Animal 父类,但我们依然可以创建一个 Cat 对象,并把它传给 f 函数,在 f 函数内成功调用它的 shout() 方法。
任何对象,只要它有 shout() 方法,哪怕这个对象是一个模块,都可以传给 f 函数并成功执行。这就是鸭子类型的本意。
上述例子,用 C++ 的多态性是无法实现的,因为 C++ 多态性建立在类的继承上,它要求 f 函数的实参是形参 Animal 的子类,这里 Cat 类并不是Animal 的子类,所以编译时直接报错。
参考
https://en.wikipedia.org/wiki/Duck_typing
「 您的赞赏是激励我创作和分享的最大动力! 」
- 原文链接:https://zhuyinjun.me/2019/duck_typing_in_python/
- 版权声明:本创作采用 CC BY-NC 4.0 国际许可协议,非商业性使用可以转载,但请注明出处(作者、链接),商业性使用请联系作者获得授权。