image
多态
问起面向对象的三大特性, 几乎每个人都能对答如流: 封装, 继承, 多态. 今天我们就要来说一说 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 编程群: 556370268, 里面都是爱好 Python 编程的小伙伴, 可以在一起学习 Python 最新知识, 一起提升技能, 有问题也能一起解决, 不管是刚学 Python 还是有一定的 Python 基础的小伙伴, 这都是个不错的选择哦.
来源: http://www.jianshu.com/p/552329048e57