多态

问起面向对象的三大特性,几乎每个人都能对答如流:封装继承多态。今天我们就要来说一说 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:多态、协议和鸭子类型的更多相关文章

  1. Python - 协议和鸭子类型

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

  2. 第7.3节 Python特色的面向对象设计:协议、多态及鸭子类型

    Python是一种多态语言,其表现特征是:对象方法的调用方只管方法是否可调用,不管对象是什么类型,从而屏蔽不同类型对象之间的差异,写出通用的代码,做出通用的编程,以适应需求的不断变化. 一.    P ...

  3. day25 面向对象之多态和鸭子类型

    1.封装方法 如何封装:给方法名称前面加上双下划线 # ATM 的取款功能 # 1.插入银行卡 2.输入密码 3.选择取款金额 4.取款 class ATM: def __insert_card(se ...

  4. python与鸭子类型

    部分参考来源:作者:JasonDing  https://www.jianshu.com/p/650485b78d11##s1 首先介绍下面向对象(OOP)的三大特征: (1)面向对象程序设计有三大特 ...

  5. PythonI/O进阶学习笔记_3.1面向对象编程_python的多态和鸭子类型

    前言: 与第一篇的面向对象内容不同的是,第一篇中的面向对象更多的是与类.对象结合起来的概念粗浅理解,就是在编程历史中诞生的一种思想方法. 这篇的面向对象编程,更多落实到在语言设计实现中,是如何体现面向 ...

  6. python之类的多态(鸭子类型 )、封装和内置函数property

    一.多态 1.什么是多态:一个类表现出的多种状态--->通过继承来实现的例如:class Animal:passclass Dog(Animal):passclass Cat(Animal):p ...

  7. python 全栈开发,Day21(抽象类,接口类,多态,鸭子类型)

    一.昨日复习 派生方法和派生属性 super 只有在子父类拥有同名方法的时候, 想使用子类的对象调用父类的方法时,才使用super super在类内 : super().方法名(arg1,..) 指名 ...

  8. python多态和鸭子类型

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

  9. Python多态、鸭子类型

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

随机推荐

  1. 一起学Vue之样式绑定

    在前端开发中,设置元素的 class 列表和内联样式是基本要求.本文主要讲解Vue开发中,样式列表和内联样式的绑定,仅供学习分享使用,如果有不足之处,还请指正. 概述 Vue操作元素的 class 列 ...

  2. elementui移动dialog

    1.在创建Vue对象时添加全局属性 Vue.directive('dialogDrag', { bind(el, binding, vnode, oldVnode) { const dialogHea ...

  3. 基于vue+leaflet+echart的足迹分享评论平台

    (其实题目是随便取的,目的只是用来证明Vue+leaflet+springboot技术栈的可行性) 效果 小专栏不支持上传视频?想看的话可以去我的知乎看最新的文章,那个应该可以.在这里 主要功能描述 ...

  4. Windows下安装和破解redis desktopmanager 2019.4

    redis可视化客户端工具:redis desktop manager 破解版链接:https://www.52pojie.cn/thread-1042770-1-1.html redis deskt ...

  5. 针对base64编码和URIEncode的一点研究

    Base64编码的作用 将任意的二进制比特串编码成由ASCii码中的64个可显示字符组成的字符串. 为什么需要base64编码? 所有的文件,本质上都是0.1组成的比特串,文本文件.二进制文件的区别只 ...

  6. Spring Cloud Finchley.SR1 版本的坑:placeholer占位符无法解析!

    接入nacos 之后,想把所有的配置丢上去. 启动程序是: @EnableDiscoveryClient @RestController @ComponentScan(basePackages = { ...

  7. 1.Ansible自动化管理工具

    1.Ansible基本概述 Ansible是一个IT自动化的配置管理工具,自动化主要体现在Ansible集成了丰富模块,丰富的功能组件, 可以通过一个命令行完成一系列的操作.进而能减少我们重复性的工作 ...

  8. English:Root "tele"

    Xx_Introduction tele mean "far" mean"faar" cognate word have tele\culture\tel\pa ...

  9. 入职小白随笔之Android四大组件——内容提供器详解(Content Provider)

    Content Provider 内容提供器简介 内容提供器(Content Provider)主要用于在不同的应用程序之间 实现数据共享的功能,它提供了一套完整的机制,允许一个程序访问另一个程序中的 ...

  10. Eclipse中如何添加相对路径的外部jar包

    在eclipse中进行java编程的时候,常常需要引用外部jar包.而采用相对路径引用jar包可以大大方便java工程的拷贝,这样使得java工程从一个路径转移到另一个路径时不用大费周章的修改外包ja ...