详细介绍Python的鸭子类型


Posted in Python onSeptember 12, 2016

鸭子类型基本定义

首先Python不支持多态,也不用支持多态,python是一种多态语言,崇尚鸭子类型。

以下是维基百科中对鸭子类型得论述:

在程序设计中,鸭子类型(英语:duck typing)是动态类型的一种风格。在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由当前方法和属性的集合决定。这个概念的名字来源于由James Whitcomb Riley提出的鸭子测试,“鸭子测试”可以这样表述:

“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”

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

鸭子类型通常得益于不测试方法和函数中参数的类型,而是依赖文档、清晰的代码和测试来确保正确使用。从静态类型语言转向动态类型语言的用户通常试图添加一些静态的(在运行之前的)类型检查,从而影响了鸭子类型的益处和可伸缩性,并约束了语言的动态特性。

python中的具体实现

下面的代码就是一个简单的鸭子类型

class duck():
  def walk(self):
    print('I walk like a duck')
  def swim(self):
    print('i swim like a duck')

class person():
  def walk(self):
    print('this one walk like a duck') 
  def swim(self):
    print('this man swim like a duck')

对于一个鸭子类型来说,我们并不关心这个对象的类型本身或是这个类继承,而是这个类是如何被使用的。我们可以通过下面的代码来调用这些类的方法。

def watch_duck(animal):
  animal.walk()
  animal.swim()

small_duck = duck()
watch_duck(small_duck)

output >> 
I walk like a duck
i swim like a duck


duck_like_man = person()
watch_duck(duck_like_man)

output >> 
this one walk like a duck
this man swim like a duck


class Lame_Foot_Duck():
  def swim(self):
    print('i am lame but i can swim')

lame_duck = Lame_Foot_Duck()
watch_duck(lame_duck)

output >>
AttributeError: Lame_Foot_Duck instance has no attribute 'walk'

watch_duck函数接收这个类的对象,然后并没有检查对象的类型,而是直接调用这个对象的走和游的方法,如果所需要的方法不存在就报错。

具体在python中鸭子类型的体现如下面的代码所示

class CollectionClass():
  lists = [1,2,3,4]
  def __getitem__(self, index):
    return self.lists[index]

iter_able_object = CollectionClass()

class Another_iterAbleClass():
  lists=[1,2,3,4]
  list_position = -1

  def __iter__(self):
    return self

  def next(self): #还有更简单的实现,使用生成器或迭代器什么的:)
    self.list_position += 1
    if self.list_position >3:
      raise StopIteration
    return self.lists[self.list_position]

another_iterable_object=Another_iterAbleClass()

print(iter_able_object[1])
print(iter_able_object[1:3])
output>>
2
[2, 3]

another_iterable_object[2]
output>>
Traceback (most recent call last):
 File "/Users/steinliber/a.py", line 32, in <module>
  another_iterable_object[2]
TypeError: 'Another_iterAbleClass' object does not support indexing

print(next(another_iterable_object))
output>>
1
print(next(another_iterable_object))
output>>
2

print(next(iter_able_object))
output>>
Traceback (most recent call last):
 File "/Users/steinliber/a.py", line 29, in <module>
  print(next(iter_able_object))
TypeError: IterAbleClass object is not an iterator

在python把上述代码的实现方法叫做protocol(协议),这些protocol可以看作是通知型的接口,它规定了调用方使用该功能要调用对象的哪些方法,被调用方要实现哪些方法才能完成这个功能。它和java中的接口区别在于java中的接口功能实现需要通过继承,继承的类必须实现接口中的所有的抽象方法,所以在Java中强调的是类型的概念,而python中的protocol更多的是通知性的,一个函数规定要实现某个功能需要调用传入对象的哪些方法,所有实现这些方法的类就可以实现这个功能。

具体从上面两个类来说,第一个类实现了__getitem__方法,那python的解释器就会把它当做一个collection,就可以在这个类的对象上使用切片,获取子项等方法,第二个类实现了__iter__next方法,python就会认为它是一个iterator,就可以在这个类的对象上通过循环来获取各个子项。一个类可以实现它有能力实现的方法,并只能被用于在它有意义的情况下。

这两个类和上面的鸭子类相比较,其实用于切边的[](它其实调用的是python的slice函数)和用于循环的iter()就相当于watch_duck函数,这些函数都接收任意类的对象,并调用实现功能所需要的对象中的方法来实现功能,若该函数中调用的方法对象里面不存在,就报错。

从上面可以看出,python鸭子类型的灵活性在于它关注的是这个所调用的对象是如何被使用的,而没有关注对象类型的本身是什么。所以在python中使用isinstance来判断传入参数的类型是不提倡的,更pythonic的方法是直接使用传入的参数,通过try,except来处理传入参数不符合要求的情况。我们应该通过传入对象的能力而不是传入对象的类型来使用该对象。

总结

以上就是Python鸭子类型的详细介绍,本文内容介绍的还是很详细的,希望对大家学习python能有一定的帮助,如果有疑问大家可以留言交流。

Python 相关文章推荐
Python 文件读写操作实例详解
Mar 12 Python
利用Python的Django框架生成PDF文件的教程
Jul 22 Python
python制作一个桌面便签软件
Aug 09 Python
Python实现列表转换成字典数据结构的方法
Mar 11 Python
Python中支持向量机SVM的使用方法详解
Dec 26 Python
关于Django ForeignKey 反向查询中filter和_set的效率对比详解
Dec 15 Python
深入了解Python在HDA中的应用
Sep 05 Python
利用Python校准本地时间的方法教程
Oct 31 Python
Python 余弦相似度与皮尔逊相关系数 计算实例
Dec 23 Python
Python编程快速上手——Excel到CSV的转换程序案例分析
Feb 28 Python
Python itertools.product方法代码实例
Mar 27 Python
Python+OpenCV图像处理——打印图片属性、设置存储路径、调用摄像头
Oct 22 Python
Python 读写文件和file对象的方法(推荐)
Sep 12 #Python
使用Python进行二进制文件读写的简单方法(推荐)
Sep 12 #Python
浅谈python对象数据的读写权限
Sep 12 #Python
python获取list下标及其值的简单方法
Sep 12 #Python
Python循环语句中else的用法总结
Sep 11 #Python
python字典键值对的添加和遍历方法
Sep 11 #Python
解决Python 遍历字典时删除元素报异常的问题
Sep 11 #Python
You might like
10个实用的PHP正则表达式汇总
2014/10/23 PHP
laravel 5.4 + vue + vux + element的环境搭配过程介绍
2018/04/26 PHP
建立良好体验度的Web注册系统ajax
2007/07/09 Javascript
javascript得到当前页的来路即前一页地址的方法
2014/02/18 Javascript
JavaScript验证电子邮箱的函数
2014/08/22 Javascript
创建js对象和js类的方法汇总
2014/12/24 Javascript
AngularJS学习笔记之TodoMVC的分析
2015/02/22 Javascript
jQuery实现hover合成事件的方法
2015/08/06 Javascript
js删除Array数组中指定元素的两种方法
2016/08/03 Javascript
AngularJs基于角色的前端访问控制的实现
2016/11/07 Javascript
vue2.0 移动端实现下拉刷新和上拉加载更多的示例
2018/04/23 Javascript
推荐一个基于Node.js的表单验证库
2019/02/15 Javascript
详解微信小程序实现仿微信聊天界面(各种细节处理)
2019/02/17 Javascript
从零撸一个pc端vue的ui组件库( 计数器组件 )
2019/08/08 Javascript
[01:10:03]OG vs EG 2018国际邀请赛淘汰赛BO3 第三场 8.23
2018/08/24 DOTA
编写Python脚本来获取mp3文件tag信息的教程
2015/05/04 Python
Python实现采用进度条实时显示处理进度的方法
2017/12/19 Python
PyTorch搭建多项式回归模型(三)
2019/05/22 Python
python查看数据类型的方法
2019/10/12 Python
Pytorch中index_select() 函数的实现理解
2019/11/19 Python
Keras 数据增强ImageDataGenerator多输入多输出实例
2020/07/03 Python
解决运行django程序出错问题 'str'object has no attribute'_meta'
2020/07/15 Python
测试工程师职业规划书
2014/02/06 职场文书
店面销售职位的职责
2014/03/09 职场文书
法律进机关实施方案
2014/03/12 职场文书
实习护士自荐信
2014/06/21 职场文书
法制宣传标语
2014/06/23 职场文书
本科毕业生应聘自荐信范文
2014/06/26 职场文书
2014年教师党员自我评价范文
2014/09/22 职场文书
2014年小学语文工作总结
2014/12/20 职场文书
经理岗位职责
2015/02/02 职场文书
工厂仓管员岗位职责
2015/04/01 职场文书
幼儿园亲子活动通知
2015/04/24 职场文书
实体类或对象序列化时,忽略为空属性的操作
2021/06/30 Java/Android
win11怎么用快捷键锁屏? windows11锁屏的几种方法
2021/11/21 数码科技
Java中try catch处理异常示例
2021/12/06 Java/Android