今天,我们来聊一聊Python中的鸭子类型(duck typing)。

编程语言具有类型概念,例如Python中有数字类型、字符串类型、布尔类型,或者更加复杂的结构,例如元组tuple、列表list、集合set和字典类型dict等等。

根据如何将类型解析并赋值给各种构造(例如变量,表达式,函数,函数参数等),编程语言可以归类为“鸭子类型”,“结构化类型”或“标称类型”。

本质上,分类决定了对象如何被解析并推断为具体的类型。

鸭子测试

鸭子类型(duck typing)语言使用鸭子测试来评估对象是否可以被解析为特定的类型。Python就是其中一种。

这个概念的名字来源于由詹姆斯·惠特科姆·莱利提出的鸭子测试。“鸭子测试”可以这样表述:

“如果看起来像鸭子,叫起来像鸭子,那么它一定是鸭子。”

If it looks like a duck and quacks like a duck, it must be a duck.

James Whitcomb Riley

鸭子类型

鸭子类型在程序设计中是动态类型的一种风格。在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由"当前方法和属性的集合"决定。

在鸭子类型中,关注点在于对象的行为,能作什么;而不是关注对象所属的类型。例如,在不使用鸭子类型的语言中,我们可以编写一个函数,它接受一个类型为"鸭子"的对象,并调用它的"走"和"叫"方法。在使用鸭子类型的语言中,这样的一个函数可以接受一个任意类型的对象,并调用它的"走"和"叫"方法。如果这些需要被调用的方法不存在,那么将引发一个运行时错误。任何拥有这样的正确的"走"和"叫"方法的对象都可被函数接受的这种行为引出了以上表述,这种决定类型的方式因此得名。

鸭子类型通常得益于"不"测试方法和函数中参数的类型,而是依赖文档、清晰的代码和测试来确保正确使用。

在常规类型中,我们能否在一个特定场景中使用某个对象取决于这个对象的类型,而在鸭子类型中,则取决于这个对象是否具有某种属性或者方法——即只要具备特定的属性或方法,能通过鸭子测试,就可以使用。

Python术语

Python术语表中的鸭子类型duck-typing

A programming style which does not look at an object’s type to determine if it has the right interface; instead, the method or attribute is simply called or used (“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(). (Note, however, that duck-typing can be complemented with abstract base classes.) Instead, it typically employs hasattr() tests or EAFP programming.

译文:

鸭子类型是一种编程风格,决定一个对象是否有正确的接口,关注点在于它的方法或属性,而不是它的类型(“如果它看起来像鸭子,像鸭子一样嘎嘎叫,那么它一定是鸭子。”)。通过强调接口而不是特定类型,设计良好的代码通过多态提高了灵活性。鸭子类型无需使用 type()isinstance() 进行检查(注意,鸭子类型可以用抽象基类来补充),相反,它通常使用 hasattr()来检查,或是 EAFP 编程。

代码示例

举个例子:

# 定义一个计算函数,接收3个入参
def calculate(a, b, c):
return (a + b) * c # 分别计算3种情况的结果
result1 = calculate(1, 2, 3)
result2 = calculate([1, 2, 3], [4, 5, 6], 2)
result3 = calculate("打工人", "打工魂", 3) # 打印3种结果
print(result1, result2, result3, sep='\n')

代码解释:在上面的例子中,我们每次调用calculate()都使用的是不同的对象(数字、列表和字符串),并且它们在继承关系中没有联系。只要输入对象支持+*方法,操作就能成功。

执行后输出:

9
[1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6]
打工人打工魂打工人打工魂打工人打工魂

这样,鸭子类型在不使用继承的情况下使用了多态。

再举个栗子:

# 鸭子类
class Duck: def quack(self):
print("这鸭子正在嘎嘎叫") def feathers(self):
print("这鸭子拥有白色和灰色的羽毛") # 人类
class Person: def quack(self):
print("这人正在模仿鸭子") def feathers(self):
print("这人在地上拿起1根羽毛然后给其他人看") # 函数/接口
def in_the_forest(duck):
duck.quack()
duck.feathers() if __name__ == '__main__': donald = Duck() # 创建一个Duck类的实例
john = Person() # 创建一个Person类的实例 in_the_forest(donald) # 调用函数,传入Duck的实例
in_the_forest(john) # 调用函数,传入Person的实例

代码运行后输出:

这鸭子正在嘎嘎叫
这鸭子拥有白色和灰色的羽毛
这人正在模仿鸭子
这人在地上拿起1根羽毛然后给其他人看

总结

鸭子类型是一个与动态类型(dynamic typing)相关的概念,其中关注的是它定义的方法,而不太关注对象的类型或所属类。当您使用鸭子类型时,你无需做检查类型。相反,您要检查给定方法或属性是否存在。

鸭子类型语言为程序员提供了最大的灵活性,程序员只需写最少量的代码。但是这些语言可能并不安全,可能会产生运行时错误,使用时需要格外注意。

参考链接:

  1. Python术语表:https://docs.python.org/3/glossary.html#term-duck-typing
  2. 维基百科:https://zh.wikipedia.org/wiki/鸭子类型

Python中的鸭子类型的更多相关文章

  1. Python 中的鸭子类型和猴子补丁

    原文链接: Python 中的鸭子类型和猴子补丁 大家好,我是老王. Python 开发者可能都听说过鸭子类型和猴子补丁这两个词,即使没听过,也大概率写过相关的代码,只不过并不了解其背后的技术要点是这 ...

  2. Python多态、鸭子类型

    一.多态 多态指的是一类事物有多种形态. 动物有多种形态:人,狗,猪 import abc class Animal(metaclass=abc.ABCMeta): #同一类事物:动物 @abc.ab ...

  3. python语言的鸭子类型和强类型语言的多态

    python语言的鸭子类型和强类型语言的多态 前面讲接口类的时候举过一个有关支付方式的例子,支付方式可以有几种,微信支付,支付宝支付,苹果支付等,这几个不同的支付都统一于支付,像这样几个类都统一于 某 ...

  4. Python中内置数据类型list,tuple,dict,set的区别和用法

    Python中内置数据类型list,tuple,dict,set的区别和用法 Python语言简洁明了,可以用较少的代码实现同样的功能.这其中Python的四个内置数据类型功不可没,他们即是list, ...

  5. Python 中的枚举类型~转

    Python 中的枚举类型 摘要: 枚举类型可以看作是一种标签或是一系列常量的集合,通常用于表示某些特定的有限集合,例如星期.月份.状态等. 枚举类型可以看作是一种标签或是一系列常量的集合,通常用于表 ...

  6. python中对象、类型和元类之间的关系

    在python中对象.类型和元类构成了一个微妙的世界. 他们有在这个世界里和平共处,相辅相成.它们遵循着几条亘古不变的定律: 1.python中无处不对象 2.所有对象都有三种特性:id.类型.值 3 ...

  7. Python中的集合类型分类和集合类型操作符解析

    集合类型    数学上,把set称作由不同的元素组成的集合,集合(set)的成员通常被称作集合元素(set elements).    Python把这个概念引入到它的集合类型对象里.集合对象是一组无 ...

  8. python多态和鸭子类型

    多态与多态性 多态指的是一类事物有多种形态,(一个抽象类有多个子类,因而多态的概念依赖于继承). 比如:文件分为文本文件,可执行文件(在定义角度) 比如 我们按下 F1 键这个动作: 如果当前在 Fl ...

  9. Python - 协议和鸭子类型

    参考: Fluent_Python - P430 wiki 这里说的协议是什么?是让Python这种动态类型语言实现多态的方式. 在面向对象编程中,协议是非正式的接口,是一组方法,但只是一种文档,语言 ...

随机推荐

  1. [LeetCode]13.罗马数字转整数(Java)

    原题地址: roman-to-integer 题目描述: 罗马数字包含以下七种字符: I, V, X, L,C,D 和 M. 字符 数值 I 1 V 5 X 10 L 50 C 100 D 500 M ...

  2. CreateEvent进程同步

    CreateEvent进程间同步   CreateEvent可以创建或是打开一个命名或是未命名的event对象. HANDLE CreateEvent(   LPSECURITY_ATTRIBUTES ...

  3. suse 12 双网卡bonding模式

    文章目录 工欲善其事,必先配静态ip 网卡bonding eth0 eth1 192.168.70.52 192.168.70.55 工欲善其事,必先配静态ip 重要的事情说三遍 修改配置之前,先备份 ...

  4. [LeetCode]1313. 解压缩编码列表

    给你一个以行程长度编码压缩的整数列表 nums . 考虑每对相邻的两个元素 [freq, val] = [nums[2i], nums[2i+1]] (其中 i >= 0 ),每一对都表示解压后 ...

  5. 实践GoF的23种设计模式:SOLID原则(上)

    摘要:本文以我们日常开发中经常碰到的一些技术/问题/场景作为切入点,示范如何运用设计模式来完成相关的实现. 本文分享自华为云社区<实践GoF的23种设计模式:SOLID原则(上)>,作者: ...

  6. [数分笔记]问题1.1 T1

    题目:非负整数a,b使得为整数,求证这个整数必是某一整数的平方.(1988年第29届国际数学奥林匹克竞赛试题) 证明:设k=,k为非负整数 1°a=b k=2a²/(1+a²)=2-2/(1+a²) ...

  7. Python:在一个Python程序中,运行另一个Python程序

    学习自: 1~3学习自如何在python中执行另一个py文件_python_脚本之家 4~6学习自Python中四种运行其他程序的方式 - hankleo - 博客园 1.os.system方法 用法 ...

  8. 获取HTML中所有图片的 URL

    /// <summary> /// 获取HTML中所有图片的 URL /// </summary> /// <param name="strHtml" ...

  9. 操作Jexus

    jws.start等命令在Jexus V5.6.3中已经合并为一个单一命令,即"jws",这是一个shell脚本文件. 命令参数与对应的功效: jws start : 启动Jexu ...

  10. consul-常用命令

    1.consul 是B/C架构.服务端和客户端包是一样的.差别在于启动时候的参数. --客户端 ./consul agent -join=172.29.2.65:8301 -bind=172.29.3 ...