- 前言
- 目标读者
- 非目标读者
- 本书的结构
- 以实践为基础
- 硬件
- 杂谈:个人的一点看法
- Python 术语表
- Python 版本表
- 排版约定
- 使用代码示例
- 第一部分 序幕
- 第 1 章 Python 数据模型
- 第二部分 数据结构
- 第 2 章 序列构成的数组
- 第 3 章 字典和集合
- 第 4 章 文本和字节序列
- 第三部分 把函数视作对象
- 第 5 章 一等函数
- 第 6 章 使用一等函数实现设计模式
- 第 7 章 函数装饰器和闭包
- 第四部分 面向对象惯用法
- 第 8 章 对象引用、可变性和垃圾回收
- 第 9 章 符合 Python 风格的对象
- 第 10 章 序列的修改、散列和切片
- 第 11 章 接口:从协议到抽象基类
- 第 12 章 继承的优缺点
- 第 13 章 正确重载运算符
- 第五部分 控制流程
- 第 14 章 可迭代的对象、迭代器和生成器
- 14.1 Sentence 类第1版:单词序列
- 14.2 可迭代的对象与迭代器的对比
- 14.3 Sentence 类第2版:典型的迭代器
- 14.4 Sentence 类第3版:生成器函数
- 14.5 Sentence 类第4版:惰性实现
- 14.6 Sentence 类第5版:生成器表达式
- 14.7 何时使用生成器表达式
- 14.8 另一个示例:等差数列生成器
- 14.9 标准库中的生成器函数
- 14.10 Python 3.3 中新出现的句法:yield from
- 14.11 可迭代的归约函数
- 14.12 深入分析 iter 函数
- 14.13 案例分析:在数据库转换工具中使用生成器
- 14.14 把生成器当成协程
- 14.15 本章小结
- 14.16 延伸阅读
- 第 15 章 上下文管理器和 else 块
- 第 16 章 协程
- 第 17 章 使用期物处理并发
- 第 18 章 使用 asyncio 包处理并发
- 第六部分 元编程
- 第 19 章 动态属性和特性
- 第 20 章 属性描述符
- 第 21 章 类元编程
- 结语
- 延伸阅读
- 附录 A 辅助脚本
- Python 术语表
- 作者简介
- 关于封面
11.6 标准库中的抽象基类
从 Python 2.6 开始,标准库提供了抽象基类。大多数抽象基类在 collections.abc 模块中定义,不过其他地方也有。例如,numbers 和 io 包中有一些抽象基类。但是,collections.abc 中的抽象基类最常用。我们来看看这个模块中有哪些抽象基类。
11.6.1 collections.abc模块中的抽象基类
标准库中有两个名为 abc 的模块,这里说的是 collections.abc。为了减少加载时间,Python 3.4 在 collections 包之外实现这个模块(在 Lib/_collections_abc.py 中),因此要与 collections 分开导入。另一个 abc 模块就是 abc(即 Lib/abc.py),这里定义的是 abc.ABC 类。每个抽象基类都依赖这个类,但是不用导入它,除非定义新抽象基类。
Python 3.4 在 collections.abc 模块中定义了 16 个抽象基类,简要的 UML 类图(没有属性名称)如图 11-3 所示。collections.abc 的官方文档中有个不错的表格,对各个抽象基类做了总结,说明了相互之间的关系,以及各个基类提供的抽象方法和具体方法(称为“混入方法”)。图 11-3 中有很多多重继承。我们将在第 12 章着重说明多重继承,讨论抽象基类时通常不用考虑多重继承。6
6Java 认为多重继承有危害,因此没有提供支持,但是提供了接口:Java 的接口可以扩展多个接口,而且 Java 的类可以实现多个接口。
图 11-3:collections.abc 模块中各个抽象基类的 UML 类图
下面详述图 11-3 中那一群基类。
Iterable、Container 和 Sized
各个集合应该继承这三个抽象基类,或者至少实现兼容的协议。Iterable 通过 __iter__ 方法支持迭代,Container 通过 __contains__ 方法支持 in 运算符,Sized 通过 __len__ 方法支持 len() 函数。
Sequence、Mapping 和 Set
这三个是主要的不可变集合类型,而且各自都有可变的子类。MutableSequence 的详细类图见图 11-2;MutableMapping 和 MutableSet 的类图在第 3 章中(见图 3-1 和图 3-2)。
MappingView
在 Python 3 中,映射方法 .items()、.keys() 和 .values() 返回的对象分别是 ItemsView、KeysView 和 ValuesView 的实例。前两个类还从 Set 类继承了丰富的接口,包含 3.8.3 节所述的全部运算符。
Callable 和 Hashable
这两个抽象基类与集合没有太大的关系,只不过因为 collections.abc 是标准库中定义抽象基类的第一个模块,而它们又太重要了,因此才把它们放到 collections.abc 模块中。我从未见过 Callable 或 Hashable 的子类。这两个抽象基类的主要作用是为内置函数 isinstance 提供支持,以一种安全的方式判断对象能不能调用或散列。7
7若想检查是否能调用,可以使用内置的 callable() 函数;但是没有类似的 hashable() 函数,因此测试对象是否可散列,最好使用 isinstance(my_obj, Hashable)。
Iterator
注意它是 Iterable 的子类。我们将在第 14 章详细讨论。
继 collections.abc 之后,标准库中最有用的抽象基类包是 numbers。下面就来介绍。
11.6.2 抽象基类的数字塔
numbers 包定义的是“数字塔”(即各个抽象基类的层次结构是线性的),其中 Number 是位于最顶端的超类,随后是 Complex 子类,依次往下,最底端是 Integral 类:
Number
Complex
Real
Rational
Integral
因此,如果想检查一个数是不是整数,可以使用 isinstance(x, numbers.Integral),这样代码就能接受 int、bool(int 的子类),或者外部库使用 numbers 抽象基类注册的其他类型。为了满足检查的需要,你或者你的 API 的用户始终可以把兼容的类型注册为 numbers.Integral 的虚拟子类。
与之类似,如果一个值可能是浮点数类型,可以使用 isinstance(x, numbers.Real) 检查。这样代码就能接受 bool、int、float、fractions.Fraction,或者外部库(如 NumPy,它做了相应的注册)提供的非复数类型。
decimal.Decimal 没有注册为 numbers.Real 的虚拟子类,这有点奇怪。没注册的原因是,如果你的程序需要 Decimal 的精度,要防止与其他低精度数字类型混淆,尤其是浮点数。
了解一些现有的抽象基类之后,我们将从零开始实现一个抽象基类,然后实际使用,以此实践白鹅类型。这么做的目的不是鼓励每个人都立即开始定义抽象基类,而是教你怎么阅读标准库和其他包中的抽象基类源码。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论