第四部分第11章,接口:从协议到抽象基类(重点讲抽象基类)

  1. 接口就是实现特定角色的方法集合。
  2. 严格来说,协议是非正式的接口(只由文档约束),正式接口会施加限制(抽象基类对接口一致性的强制)。
  3. 在Python中,“X类对象”、“X协议”、“X接口”都是一个意思。如“文件类对象”、“可迭代对象”,指的不是特定的类。
  4. 一个类可能会实现多个接口,从而让实例扮演多个角色。
  5. Python语言没有interface关键字,而且除了抽象基类,每个类都有接口:类实现或继承的公开属性(方法或数据属性),包括特殊方法,如__getitem__或__add__。受保护的属性和私有属性不在接口中:即便是“受保护的”属性也只是采用命名约定实现的(单个前导下划线,可以轻松访问)。不要违背这些约定。

1. Python喜欢序列

例子1. 只实现序列协议的一部分,即只定义__getitem__,但这样足够访问元素、迭代和使用in运算符。

鉴于序列协议很重要,如果没有__iter__和__contains__方法,Python会调用__getitem__方法,设法让迭代和in运算符可用。

class Foo:
def __getitem__(self, pos):
return range(0, 30, 10)[pos] f = Foo()
# 1. f[]调用__getitem__
print(f[1])
# 2. 没有__iter__方法,转而调用__getitem__方法,传入从0开始的整数索引,尝试迭代对象(这是一种后备机制)
for i in f:
print(i)
# 3. 没有__contains__方法,转而调用__getitem__方法
print(20 in f)

Python中的迭代是鸭子类型的一种极端形式:为了迭代对象,解释器会尝试调用两个不同的方法。

2. 使用猴子补丁在运行时实现协议

2.1 如果遵守既定协议,很有可能增加利用现有的标准库的可能性,得益于鸭子类型。例如FrenchDeck实例的行为像序列,就可以用random.shuffle就地地打乱序列。

2.2 猴子补丁:在运行时修改类或模块,而不改动源码。

2.3 例如在FrenchDeck使用猴子补丁之前,虽然部分行为表现得像序列,但仍然不能使用random.shuffle(错误提示为'FrenchDeck' object does not support item assignment)。原因是shuffle函数要调换集合中元素的位置,而FrenchDeck只实现了不可变的序列。可变的序列还必须提供__setitem__方法。

2.4 在运行时修正FrenchDeck为可变序列,让random.shuffle能处理

def set_card(deck, position, card):
deck._cards[position] = card #猴子补丁,把自定义的函数赋值给__setitem__,把它变成可变的。
FrenchDeck.__setitem__ = set_card

2.5 猴子补丁缺点:打补丁的代码与要打补丁的程序耦合十分紧密,而且往往要处理隐藏和没有文档的部分。(例如这里的关键是set_card函数要知道deck对象有一个名为_cards属性,而且_cards的值必须是可变序列)

2.6 还要注意的是:

3. 直接使用抽象基类,而不只将其当作文档。

3.1 Python的抽象基类有一个重要的实用优势:可以实用register类方法在终端用户的代码中把某个类“声明“为一个抽象基类的”虚拟“子类。做register时,我们保证注册的类忠实地实现了抽象基类定义的接口,而Python会相信我们,从而不做检查。如果我们说谎了,那么常规的运行时异常会把我们捕获。(注册的类不会从抽象基类中继承任何方法或属性)

3.2 有时,为了让抽象基类识别子类,甚至不用注册。无需注册,abc.Sized也能把Struggle识别为自己的子类,只要实现了特殊方法__len__即可。

class Struggle:
def __len__(self):
return 23 from collections import abc
#True
print(isinstance(Struggle(), abc.Sized))

3.3 检查是不是”序列“,那就这样做:

isinstance(the_arg, collections.abc.Sequence)

3.4 最好避免自定义抽象基类。如果自己想创建新的抽象基类,先试着通过常规的鸭子类型来解决问题。

3.5 colletions.namedtuple处理field_names参数的原理

field_names的值可以是单个字符串,以空格或者逗号分隔标识符,也可以是一个标识符序列。

例子3.5.1 用鸭子类型处理单个字符串或由字符串组成的可迭代对象

#1. 假设是单个字符串风格(EAFP风格)
try:
#2. 把逗号替换成空格,然后拆分成名称列表。
field_names =
field_names.replace(',', ' ').split()
#3. 如果field_names看起来不像是字符串
except AttributeError:
#4. 假设已经是由名称组成的可迭代对象
pass
#5. 为了确保的确是可迭代对象,也为了保存一份副本,使用所得值创建一个元组。
field_names = tuple(field_names)

4. !继承抽象基类

例子1. 继承抽象基类,抽象基类会对子类作出限制。



导入时(加载并编译.py模块时),Python不会检查抽象方法的实现。在运行时实例化子类才会真正检查。

例子2. 假如我有一个FrenchDeck2的类继承MutableSequence抽象基类。



例子3. 我们可以覆盖子类中的从抽象基类中继承的方法,以更高效的方式重新实现。

例如,__cotains__方法会全面扫描序列。如果你定义的序列按顺序保存元素,那就可以重新定义__cotains__方法,使用bisect函数做二分查找,从而提升搜索速度。

5. !标准库中的抽象基类

collections.abc中的抽象基类最常用。numbers和io包中也有一些抽象基类。

5.1 collections.abc模块中的抽象基类

  1. 官方文档:https://docs.python.org/3/library/collections.abc.html
  2. 简要的UML类图(没有属性名称)





5.2 numbers的抽象基类

  1. https://docs.python.org/3/library/numbers.html
  2. numbers包定义的是”数字塔“(各个抽象基类的层次结构是线性的),其中Number是位于最顶端的超类,依次往下,最底端的是Integral类。

    Number、Complex、Real、Rational、Integral.
  3. 因此,如果想检查一个数是不是整数,可以使用isinstance(x, numbers.Integral),

6. 定义并使用一个抽象基类

  1. 最好不要自定义抽象基类
  2. 此处自定义抽象基类的目的是学会怎么阅读标准库和其他包中的抽象基类源码。
  3. 中文电子书P491

7. Python使用register

  1. 当装饰器用:@Sequence.register
  2. 当作函数用:Sequence.register(tuple)

8. 鹅(本章提到了goose typing)的行为有可能像鸭子

  1. 即便不注册,抽象基类也能把一个类别识别为虚拟子类。
class Struggle:
def __len__(self):
return 23 from collections import abc
isinstance(Struggle(), abc.Sized)
issubclass(Struggle, abc.Sized)

原理:



Fluent_Python_Part4面向对象,11-iface-abc,协议(接口),抽象基类的更多相关文章

  1. 【Python】【元编程】【从协议到抽象基类】

    """class Vector2d: typecode = 'd' def __init__(self,x,y): self.__x = float(x) self.__ ...

  2. Python 接口:从协议到抽象基类

    p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 15.0px Helvetica } 抽象基类的常见用途:实现接口时作为超类使用.然后,说明抽象基类如何检查 ...

  3. python 用abc模块构建抽象基类Abstract Base Classes

    见代码: #!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2018/08/01 16:58 from abc import ABCMet ...

  4. Python抽象基类:ABC谢谢你,因为有你,温暖了四季!

    Python抽象基类:ABC谢谢你,因为有你,温暖了四季! Python抽象基类:ABC谢谢你,因为有你,温暖了四季! 实例方法.类方法和静态方法 抽象类 具名元组 参考资料 最近阅读了<Pyt ...

  5. python3:iterable, iterator, generator,抽象基类, itertools的使用。

    目录: iterable对象 iterator对象, 数据类型Iterator类 数据类型Generator类. 生成器表达式 collections.abc:容器的抽象基类.用于判断具体类. ite ...

  6. Python中的抽象基类

    1.说在前头 "抽象基类"这个词可能听着比较"深奥",其实"基类"就是"父类","抽象"就是&quo ...

  7. 6、面向对象以及winform的简单运用(抽象基类与接口)

    抽象类与抽象方法 1.书写规范: 在类前面加上abstract关键字,就成为了抽象类:在一个方法前面加上abstract关键字,就成为了抽象方法(抽象方法不能有实现方法,直接在后面加分号) 例: ab ...

  8. NSObject class和NSObject protocol的关系(抽象基类与协议)

    [转载请注明出处] 1.接口的实现 对于接口这一概念的支持,不同语言的实现形式不同.Java中,由于不支持多重继承,因此提供了一个Interface关键词.而在C++中,通常是通过定义抽象基类的方式来 ...

  9. PythonI/O进阶学习笔记_3.2面向对象编程_python的继承(多继承/super/MRO/抽象基类/mixin模式)

    前言: 本篇相关内容分为3篇多态.继承.封装,这篇为第二篇 继承. 本篇内容围绕 python基础教程这段: 在面向对象编程中,术语对象大致意味着一系列数据(属性)以及一套访问和操作这些数据的方法.使 ...

随机推荐

  1. 如何在Word中批量选中特定文本

    如何在Word中批量选中特定文本 举个例子,我们对如下文本进行操作,将文本中所有的“1111111”标红,所有的“2222222”标绿,所有的“3333333”标蓝 在Word中找到“查找”下的“高级 ...

  2. crowdfunding项目01——感人的错误

    四十搭环境,半个小时下载jar包,网速感人,一个半小时找bug真是感动 首先SSM项目,主要功能进行增删改查 建立父工程和子工程,产生依赖关系 父工程:pom 子工程:jar.war(web工程) 错 ...

  3. sql 根据两点经纬度算出两点之间距离

    select (sqrt( ( ((121.544685-longitude)*PI()*12656*cos(((31.134857+latitude)/2)*PI()/180)/180) * ((1 ...

  4. jenkins部署到Tomcat下

    一.首选要下载与安装jdk,因为需要在Java环境下面运行,安装好jdk后配置java环境变量(jdk环境变量的配置网上很多) 我本人安装的是jdk1.8 二.安装好jdk后,需要下载tomcat,这 ...

  5. Python(三):环境及其配置

    一,PYTHONPATH 默认的Python模块搜索路径,可以将路径指向anaconda3,需要开发者自己设置 二,PYTHONHASHSEED 如果该环境变量被设定为 random ,相当于 -R ...

  6. 最大/最小de K个数/第K个数

    题目 在未排序的数组中找到第 k 个最大的元素.请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素. 思路 堆排序 收获 用优先队列实现最大最小堆 注意下列代码中优先队列 ...

  7. 转载:EQ

    https://blog.csdn.net/qiumingjian/article/details/46326269 https://blog.csdn.net/sszhouplus/article/ ...

  8. C#中如何将字符串或者字符串数组写入文本文件

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  9. Docker Compose 使用示例

    一般步骤 1.定义Dockerfile,方便迁移到任何地方: 2.编写docker-compose.yml文件: 3.运行docker-compose up启动服务 示例 准备工作:提前下载好镜像: ...

  10. 生成树计数 UVA 10766

    //本题题意:首先每个点之间都可达,然后m列举出不可达的,求出最多的生成树方案: //k这个变量是没用的. //公式:ans矩阵=度矩阵-建边矩阵: //度矩阵是当i==j时的,建边矩阵于平时定义可达 ...