详细介绍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列表去重的二种方法
Feb 14 Python
Python中实现结构相似的函数调用方法
Mar 10 Python
Python中的变量和作用域详解
Jul 13 Python
python中文乱码不着急,先看懂字节和字符
Dec 20 Python
python实现AES加密和解密
Mar 27 Python
Django框架用户注销功能实现方法分析
May 28 Python
使用Windows批处理和WMI设置Python的环境变量方法
Aug 14 Python
浅析python 定时拆分备份 nginx 日志的方法
Apr 27 Python
Pyinstaller加密打包应用的示例代码
Jun 11 Python
Python Charles抓包配置实现流程图解
Sep 29 Python
Python实现迪杰斯特拉算法并生成最短路径的示例代码
Dec 01 Python
Python Socket编程详解
Apr 25 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
自制短波长线天线频率预选器 - 成功消除B2K之流的镜像
2021/03/02 无线电
ThinkPHP3.1新特性之多层MVC的支持
2014/06/19 PHP
php中使用in_array() foreach array_search() 查找数组是否包含时的性能对比
2015/04/14 PHP
PHP YII框架开发小技巧之模型(models)中rules自定义验证规则
2015/11/16 PHP
PHP registerXPathNamespace()函数讲解
2019/02/03 PHP
PHP 范围解析操作符(::)用法分析【访问静态成员和类常量】
2020/04/14 PHP
javascript 进度条 实现代码
2009/07/30 Javascript
在一个浏览器里呈现所有浏览器测试结果的前端测试工具的思路
2010/03/02 Javascript
JavaScript检查某个function是否是原生代码的方法
2014/08/20 Javascript
node.js中的buffer.Buffer.isBuffer方法使用说明
2014/12/14 Javascript
简述AngularJS的控制器的使用
2015/06/16 Javascript
javascript动画算法实例分析
2015/07/31 Javascript
freemarker判断对象是否为空的方法
2015/08/13 Javascript
js replace(a,b)之替换字符串中所有指定字符的方法
2016/08/17 Javascript
js调用父框架函数与弹窗调用父页面函数的简单方法
2016/11/01 Javascript
ionic2 tabs 图标自定义实例
2017/03/08 Javascript
seajs下require书写约定实例分析
2018/05/16 Javascript
Vue数据驱动表单渲染,轻松搞定form表单
2019/07/19 Javascript
详解微信小程序「渲染层网络层错误」的解决方法
2021/01/06 Javascript
[02:25]专访DOTA2负责人Erik 国际邀请赛暂不会离开西雅
2014/07/21 DOTA
在Python的web框架中中编写日志列表的教程
2015/04/30 Python
python链接Oracle数据库的方法
2015/06/28 Python
python数据结构之链表详解
2017/09/12 Python
微信跳一跳python辅助软件思路及图像识别源码解析
2018/01/04 Python
python中单下划线_的常见用法总结
2018/07/10 Python
Python+numpy实现矩阵的行列扩展方式
2019/11/29 Python
金智子午JAVA面试题
2015/09/04 面试题
应届生骨科医生求职信
2013/10/31 职场文书
基层党支部公开承诺书
2014/05/29 职场文书
学生安全责任书模板
2014/07/25 职场文书
作风年建设汇报材料
2014/08/14 职场文书
纪念九一八事变演讲稿:青少年应树立远大理想
2014/09/14 职场文书
2014年商场工作总结
2014/11/22 职场文书
中国文明网2015年“向国旗敬礼”活动网上签名寄语
2015/09/24 职场文书
《秋天的图画》教学反思
2016/02/19 职场文书
apache虚拟主机配置的三种方式(小结)
2022/07/23 Servers