前言

在实习期间,由于工作需要首次接触了Python这门语言,由于学习和使用的时间非常短,所以当时认为,作为一门解释性语言,在做Web开发方面,Python和PHP的差别不大,甚至在一些应用场景上没有PHP来的简单粗暴。后来,在导师的推荐下,通过《流畅的Python》又一次深入的学习了Python,大致从数据结构、函数、面向对象和控制流程这几个部分深入的学习了这一门语言,对其中作为一等公民的函数和面向对象的实现留下了深刻的映像,开始体会到这门语言的独有魅力。
这本书中,对于Python面向对象的实现机制,介绍了一个非常有趣的概念:鸭子类型(duck typing)。本文将基于鸭子类型这一概念来记录和分享一些Python的学习体会,同时结合过去对Java的学习,比较这两种风格截然不同的语言。同时,基于Java的面向对象,提出鸭子类型背后的两种面向对象思想:多态和范型

什么是鸭子类型

在维基百科中是这样定义的:

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

而鸭子类型这一名字出自James Whitcomb Riley在鸭子测试中提出的如下的表述:

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

简单归纳就是:对象的类型不再由继承等方式决定,而由实际运行时所表现出的具体行为来决定。
这个概念在解释性语言中还是非常容易理解的,因为解释性语言在定义函数的参数时是无法指定具体参数类型的,另一方面,参数的类型是在解释执行时才能确定,不像类Java语言一样,在编译期编译器就可以确定参数类型,从而在多态模式下确定函数的执行版本了。
另一方面,Python并未沿用Java语言中复杂的接口和类的继承框架,因此对于面向对象有独有的实现风格,接下来的内容和今后更新的博客将详细探讨这个问题。

一个鸭子类型的实例

存在这样一个应用场景:在电子商务系统中,华为旗舰店为了进行商品促销,设计了多套促销方案,而我们需要为该店实现价格计算功能。那么,这个功能可简单的抽象为一个函数:discount_compute(user, product, num),该函数主要由3个参数构成,用户信息user,商品信息product,购买数量num,同时为了实现促销,商品类还应分别实现两个功能:折扣促发条件condition函数和折扣方式get_discount函数。而这时,苹果旗舰店也要进行促销活动,显然苹果旗舰店不是华为旗舰店,但是在鸭子类型的编程模式下,苹果旗舰店只要根据协议,在对应商品类中实现condition函数get_discount函数就可以直接使用我们设计好的价格计算功能了。
哈哈,通过上面的举例描述,相信学过Java或C++的同学一眼就能看出,这不就是多态嘛。利用接口或者超类,将统一的行为进行抽象,再由具体的子类实现进行不同的功能扩展。没错,鸭子类型的编程风格,在实际的应用场景中,确实发挥的是一种面向对象中多态的功能。
Python作为一种解释性语言,相比于PHP的魅力也在这里,实现出一种自己独有的面向对象风格。下一章,将详细介绍Python中的鸭子风格的体现。

Python中的鸭子类型

首先,在Python中,面向对象的多态和抽象是通过把协议当作正式接口来实现的。关于这一点,最明显的特征是对于私密(private)属性Python没有具体的关键字进行支持,也就是说,在Python中,你定义的对象属性始终可以被修改。对此,Python社区只是通过提倡使用一种命名规则来声明私密变量,具体地,就是在变量前加上一个或两个_
因此,我们将在Python的很多特性中,不断看到协议这个概念,但是不同于Java,各种协议是没有具体的接口来进行支持的,甚至在Python中连接口申明的关键字都没有。接下来我们将详细的来讨论Python中的鸭子类型以及有关协议的实现。

a. 序列的实现

基本序列协议: 实现__len____getitem__方法
典型的,在Python中,如果希望使用序列的相关功能,例如通过索引进行随机访问list[index],使用一些Python提供的模板方法,例如序列上的排序方法sorted或者是以序列为参数的random.shuffle,最简单的方式都是实现序列协议,采用鸭子类型模式,能够以最低的开发成本使用这些功能。
例如,我们实现了一个简单的纸牌类FrenchDeck,现在需要能够添加洗牌功能,那么只需要让纸牌类实现序列协议即可,示例代码如下:

import collectionsCard = collections.namedtuple('Card', ['rank', 'suit'])class FrenchDeck:ranks = [str(n) for n in range(2, 11)] + list('JQKA')suits = 'spades diamonds clubs hearts'.split()def __init__(self):self._cards = [Card(rank, suit) for suit in self.suitsfor rank in self.ranks]def __len__(self):return len(self._cards)def __getitem__(self, position):return self._cards[position]

若不使用序列协议,那么洗牌的功能,将是一个重复造轮子的过程,同时,如果处理不好随机化问题,还会带来新的风险和漏洞。
在这个示例中,纸牌类本身和序列没有任何关系,但是只要实现了序列协议,有了满足序列的相关行为,就成为了序列,因此,就可以正常和安全的使用相关内置方法。
也就是说,对于Python提供的很多以列表为参数的内置方法,我们都可以将实现了列表协议的不同类传递进行去,获对应功能。上面的描述可能有点绕,但是仔细一想,我们似乎又感受到另外一种面向对象的概念,那就是范型。没错,这不就正是范型的定义嘛,我们传递给方法的参数,没有继承相同的父类,甚至没有接口的概念,但是都能得到正确的处理。

b. 切片的实现

基本切片协议: 实现__getitem__方法
说到切片,这可是Python的一种高级使用方法,同时在其他的语言里面基本看不到的一种概念,我们可以通过切片功能,实现列表的灵活应用。
我们发现当实现了列表协议以后,已经能够正常使用分片了。在上述的纸牌例子中,我们已经可以正常使用分片的所有功能。
这里需要注意的是,在这个例子中,由于__getitem__方法是直接对内置类进行操作,那么可返回正常的对象,但是若操作的是序列本身,则需要进行特殊处理,具体示例如下:

class Vector:typecode = 'd'def __init__(self, components):self._components = array(self.typecode, components)def __iter__(self):return iter(self._components)def __repr__(self):components = reprlib.repr(self._components)components = components[components.find('['):-1]return 'Vector({})'.format(components)def __len__(self):return len(self._components)def __getitem__(self, index):return self._components[index]

该示例的运行结果如下:

>>> from test1 import Vector
>>> v = Vector([1,2,3,4])
>>> v[:1]
array('d', [1.0])
>>> v[2:3]
array('d', [3.0])
>>> v[1:]
array('d', [2.0, 3.0, 4.0])

返回结果是array类型而非Vector类型,因此需要对__getitem__方法进行特殊处理,将返回结果统一为Vector类,可通过如下代码进行改进:

def __getitem__(self, index):cls = type(self)  if isinstance(index, slice):  return cls(self._components[index])  elif isinstance(index, numbers.Integral):  return self._components[index]  else:msg = '{cls.__name__} indices must be integers'raise TypeError(msg.format(cls=cls))

c. 其他鸭子类型

Python中,通过对内置特殊函数的实现(前后双_函数),从而获得Python原生的支持,其中对一元中缀表达式的覆盖,对常规加法操作、乘法操作等的覆盖,皆是鸭子类型的表现形式。这些例子还有非常多,至此不再一一列举。

总结

此篇博客以鸭子类型这一编程风格为中心,探讨了Python的面向对象的部分特性,其中对鸭子类型的本质进行了分析和解释,更多的是自己对Python面向对象的一些理解。
同时,结合博主过去对Java面向对象的理解,讨论了Python面向对象的两大重要概念:多态和范型。同时结合《流畅的Python》中提供的两大经典示例进行了概念的分析。从而加深了对鸭子类型这一编程风格的理解,如若有不正之处,望各位能指出~
最后,今后本博客将继续更新:作为一等公民的函数、控制流程和元编程等章节,望与各位Python学习者一同成长,还望各位继续关注本博客,谢谢各位了?

Python学习笔记——鸭子类型(duck typing)相关推荐

  1. Python笔记 · 鸭子类型 / Duck Typing

    1. 问题的由来 我初次意识到鸭子类型的存在是在学习Sklearn时,在<Hands-On Machine Learing>一书的第二章,作者提供了一个自定义的Tansformer,使用自 ...

  2. Python编程基础:第四十九节 鸭子类型Duck Typing

    第四十九节 鸭子类型Duck Typing 前言 实践 前言 本节我们一起学习一个非常有趣的知识点:鸭子类型.有这么一句话:If it walks like a duck, and it quacks ...

  3. 【Python】浅谈 鸭子类型 (Duck Typing)

    目录 一.来源 二.说明 三.举例 四.不足 一.来源 在程序设计中,鸭子类型 (duck typing) 是动态类型的一种风格.在此风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口, ...

  4. duck typing java_编程语言中的鸭子类型 Duck Typing

    来源 | https://segmentfault.com/a/1190000019607240 1.什么是鸭子类型(duck typing) 百度百科是这样解释的: 这是程序设计中的一种类型推断风格 ...

  5. 编程语言中的鸭子类型 Duck Typing

    来源 | https://segmentfault.com/a/1190000019607240 1.什么是鸭子类型(duck typing) 百度百科是这样解释的: 这是程序设计中的一种类型推断风格 ...

  6. Python学习笔记之类型判断,异常处理,终止程序操作小结

    Python学习笔记之类型判断,异常处理,终止程序操作小结 运行结果: 这里有我自己整理了一套最新的python系统学习教程,包括从基础的python脚本到web开发. 爬虫.数据分析.数据可视化.机 ...

  7. 鸭子类型duck typing(动态)

    在程序设计中,鸭子类型(duck typing)是动态类型的一种风格.在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由当前方法和属性的集合决定.这个概念的名字来源于由Ja ...

  8. Python学习笔记 - 变量类型(1)

    变量类型 严格意义上讲,python只有一个类型 python的标准数据类型有六种 数字类型Number 字符串类型 str 列表 list 元组 tuple 字典 dict 集合 set 数字类型 ...

  9. [转载] Python学习笔记 String类型常用方法

    参考链接: Python string.zfill()方法 把字符串的第一个字符改为大写 casefold() 把整个字符串的所有字符改为小写 center(width) 将字符串居中,并使用空格填充 ...

最新文章

  1. QoS策略及通过BGP传播—Vecloud微云
  2. java 面向对象 — 继承
  3. android 更改edittext内容,Android如何实时更改edittext的内容
  4. file结构体中private_data指针的疑惑
  5. U盘安装Linux安装报错及解决方案
  6. session放入缓存(redis)、DB
  7. 2.描述性统计的matlab 实现
  8. Android 学习之Fragment生命周期
  9. IBM推出新功能 加速AI应用
  10. 目标检测数据集标注-VOC格式
  11. 微信小程序资料集(上)
  12. 投资高手三十年投资经验总结的18条真谛
  13. 全网最火爆,最详细Docker与自动化测试讲解,看完觉得我又行了
  14. C语言-arc画一弧线功能
  15. BaiduMap---百度地图官方Demo之OpenGL绘制功能(介绍如何使用OpenGL绘制在地图中进行绘制)
  16. iWebShop 电商项目实战004----功能测试
  17. textblob 情感分析_使用TextBlob进行远程学习的推文中的情感分析
  18. 微信公众号中实现实时语音转写
  19. 攻城狮生活-3 奇怪的司机
  20. codeforces 771 A

热门文章

  1. 移动web:如何下载和使用Normalize.css?
  2. js怎么输出友情链接html,JTBC友情链接JS生成的使用方法
  3. 免费分享一个springboot+vue学生选课管理系统,挺漂亮的
  4. C语言图书租赁管理系统
  5. 【图解算法数据结构】数据结构篇 + Java代码实现
  6. 火山安卓组件之花式按钮
  7. Exception: Dataset not found.解决办法
  8. phpstudy配置oracle,【phpstudy】安装Oracle 客户端 并连接
  9. oracle ebs实施伙伴,Oracle EBS 实施方法论扫盲: 什么是CRP(Confrence Room Pilot)
  10. Android 自定义View总结