Python实现多态、协议和鸭子类型的代码详解


Posted in Python onMay 05, 2019

多态

问起面向对象的三大特性,几乎每个人都能对答如流:封装、继承、多态。今天我们就要来说一说 Python 中的多态。

所谓多态:就是指一个类实例的相同方法在不同情形有不同表现形式。多态机制使具有不同内部结构的对象可以共享相同的外部接口。这意味着,虽然针对不同对象的具体操作不同,但通过一个公共的类,它们(那些操作)可以通过相同的方式予以调用。

我在《Python 中的设计模式详解之:策略模式》一文中详细描述了策略模式的实现,而策略模式就是典型的多态应用。

之前的代码我就不贴了,大家可以去原文中查看。我依然还是以商品折扣的经典举例。策略模式一文中,传统的策略模式实现方式我也是用 Python 代码实现的,在 java 或 C# 等语言中,实现方式也差不多。以下是 C# 代码,我只列了个架子:

interface Promotion
{
 double discount(Order order);
}
class FidelityPromo : Promotion // 第一个具体策略
{
 // 为积分为1000或以上的顾客提供5%折扣
 public double discount(Order order)
 {
  ...
 }
}
class BulkItemPromo : Promotion // 第二个具体策略
{
 //单个商品为20个或以上时提供10%折扣
 public double discount(Order order)
 {
  ...
 }
}
class LargeOrderPromo : Promotion // 第三个具体策略
{
 //订单中的不同商品达到10个或以上时提供7%折扣
 public double discount(Order order)
 {
  ...
 }
}

可以看到,首先要有一个接口(Promotion),然后各个策略去实现这个接口。然而,Python 语言没有 interface 关键字,就是说,Python 里没有像 java、C# 一样的接口。

在策略模式一文的实现中,使用了抽象基类(Abstract Base Class,ABC)来实现接口,这主要是为了写法上看起来和 java、C# 等语言更加的像,易于有这些语言基础的同学理解和对比。

抽象基类是在 Python 语言诞生 15 年后,Python 2.6 才引入的。这里我们不详细介绍抽象基类,因为即便现在也很少有代码使用抽象基类。对于多态,Python 有更好的实现方式——鸭子类型(duck typing)。

协议和鸭子类型

所谓 鸭子类型 就是:如果一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么它就是鸭子。这个概念的名字来源于 James Whitcomb Riley 提出的鸭子测试。

初次看到这个描述的小伙伴一定一头雾水,为了理解鸭子类型,我们不得不提到另一个名词——协议。

在面向对象编程中,协议是非正式的接口,是一组方法,只由文档和约定定义,因此,协议不能像正式接口那样施加强制性约束。而 Python 的哲学就是尽量支持基本协议。

翻译成人话,就是:Python 中没有接口,在需要使用接口的地方,就用协议代替。所谓协议,其实就是一组方法,和接口中定义的方法一个意思。只不过协议是不是强制性的约定,如果你不遵守协议,那么也没关系,运行时报错就是了。

这样就好理解鸭子类型了,“如果一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子” 这就表示已经遵守了协议,“那么它就是鸭子”,意味着你可以在其他用到“鸭子”的地方,用“这只鸟”替换。这不就是多态吗?

用“鸭子类型”来实现策略模式也很简单,删掉抽象基类就可以了。(这就是为什么抽象基类很少使用的原因,因为删掉代码也一样正确啊。)有兴趣的小伙伴可以自己尝试一下代码。

Python 中的协议举例

Python 中有很多的协议,比如迭代器协议,任何实现了 __iter__ 和 __next__ 方法的对象都可称之为迭代器,但对象本身是什么类型不受限制,这得益于鸭子类型。

from collections import Iterable
from collections import Iterator

class MyIterator:
 def __iter__(self):
  pass
 def __next__(self):
  pass
print(isinstance(MyIterator(), Iterable)) 
print(isinstance(MyIterator(), Iterator))

输出:

True
True

结语

鸭子类型是编程语言中动态类型语言中的一种设计风格,一个对象的特征不是由父类决定,而是通过对象的方法决定的。

Python 不是不支持多态,而是 Python 本身就是一门多态的语言。

Python 相关文章推荐
Python类的基础入门知识
Nov 24 Python
在Apache服务器上同时运行多个Django程序的方法
Jul 22 Python
python 爬取微信文章
Jan 30 Python
Python的Django框架中forms表单类的使用方法详解
Jun 21 Python
Python实现通过文件路径获取文件hash值的方法
Apr 29 Python
Pandas:Series和DataFrame删除指定轴上数据的方法
Nov 10 Python
详解如何用django实现redirect的几种方法总结
Nov 22 Python
Python版名片管理系统
Nov 30 Python
Django+Xadmin构建项目的方法步骤
Mar 06 Python
Django利用cookie保存用户登录信息的简单实现方法
May 27 Python
详解python opencv、scikit-image和PIL图像处理库比较
Dec 26 Python
Matplotlib使用字符串代替变量绘制散点图的方法
Feb 17 Python
用uWSGI和Nginx部署Flask项目的方法示例
May 05 #Python
基于python实现高速视频传输程序
May 05 #Python
Python远程视频监控程序的实例代码
May 05 #Python
Python统计一个字符串中每个字符出现了多少次的方法【字符串转换为列表再统计】
May 05 #Python
20行python代码实现人脸识别
May 05 #Python
使用python实现mqtt的发布和订阅
May 05 #Python
Python向excel中写入数据的方法
May 05 #Python
You might like
PHP自动生成月历代码
2006/10/09 PHP
PHP如何得到当前页和上一页的地址?
2006/11/27 PHP
phpfans留言版用到的数据操作类和分页类
2007/01/04 PHP
php 分库分表hash算法
2009/11/12 PHP
PHP程序员最常犯的11个MySQL错误小结
2010/11/20 PHP
PHP实现动态添加XML中数据的方法
2018/03/30 PHP
常规表格多表头查询示例
2014/02/21 Javascript
快速解决jquery.touchSwipe左右滑动和垂直滚动条冲突
2016/04/15 Javascript
JavaScript 对象详细整理总结
2016/09/29 Javascript
axios全局请求参数设置,请求及返回拦截器的方法
2018/03/05 Javascript
webpack打包node.js后端项目的方法
2018/03/10 Javascript
在小程序中使用Echart图表的示例代码
2018/08/02 Javascript
浅析Vue实例以及生命周期
2018/08/14 Javascript
Vue.js单向绑定和双向绑定实例分析
2018/08/14 Javascript
vue调用本地摄像头实现拍照功能
2020/08/14 Javascript
小程序实现密码输入框
2020/11/16 Javascript
[49:56]VG vs Optic 2018国际邀请赛小组赛BO2 第一场 8.19
2018/08/21 DOTA
详解C++编程中一元运算符的重载
2016/01/19 Python
Ubuntu下安装PyV8
2016/03/13 Python
Python实现可获取网易页面所有文本信息的网易网络爬虫功能示例
2018/01/15 Python
python线程池threadpool使用篇
2018/04/27 Python
Python下使用Scrapy爬取网页内容的实例
2018/05/21 Python
python生成以及打开json、csv和txt文件的实例
2018/11/16 Python
Django 中间键和上下文处理器的使用
2019/03/17 Python
Python使用matplotlib绘制圆形代码实例
2020/05/27 Python
Python替换NumPy数组中大于某个值的所有元素实例
2020/06/08 Python
如何使用html5与css3完成google涂鸦动画
2012/12/16 HTML / CSS
潘多拉珠宝俄罗斯官方网上商店:PANDORA俄罗斯
2020/09/22 全球购物
房产委托公证书样本
2014/04/04 职场文书
挂牌仪式策划方案
2014/05/18 职场文书
十佳好少年事迹材料
2014/08/21 职场文书
婚礼新人答谢词
2015/01/04 职场文书
关于颐和园的导游词
2015/01/30 职场文书
《鸟的天堂》教学反思
2016/02/19 职场文书
用Python进行栅格数据的分区统计和批量提取
2021/05/27 Python
SpringBoot整合Mybatis Generator自动生成代码
2021/08/23 Java/Android