- 前言
- 目标读者
- 非目标读者
- 本书的结构
- 以实践为基础
- 硬件
- 杂谈:个人的一点看法
- 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 术语表
- 作者简介
- 关于封面
3.6 子类化 UserDict
就创造自定义映射类型来说,以 UserDict 为基类,总比以普通的 dict 为基类要来得方便。
这体现在,我们能够改进示例 3-7 中定义的 StrKeyDict0 类,使得所有的键都存储为字符串类型。
而更倾向于从 UserDict 而不是从 dict 继承的主要原因是,后者有时会在某些方法的实现上走一些捷径,导致我们不得不在它的子类中重写这些方法,但是 UserDict 就不会带来这些问题。6
6关于从 dict 或者其他内置类继承到底有什么不好,详见 12.1 节。
另外一个值得注意的地方是,UserDict 并不是 dict 的子类,但是 UserDict 有一个叫作 data 的属性,是 dict 的实例,这个属性实际上是 UserDict 最终存储数据的地方。这样做的好处是,比起示例 3-7,UserDict 的子类就能在实现 __setitem__ 的时候避免不必要的递归,也可以让 __contains__ 里的代码更简洁。
多亏了 UserDict,示例 3-8 里的 StrKeyDict 的代码比示例 3-7 里的 StrKeyDict0 要短一些,功能却更完善:它不但把所有的键都以字符串的形式存储,还能处理一些创建或者更新实例时包含非字符串类型的键这类意外情况。
示例 3-8 无论是添加、更新还是查询操作,StrKeyDict 都会把非字符串的键转换为字符串
import collections class StrKeyDict(collections.UserDict): ➊ def __missing__(self, key): ➋ if isinstance(key, str): raise KeyError(key) return self[str(key)] def __contains__(self, key): return str(key) in self.data ➌ def __setitem__(self, key, item): self.data[str(key)] = item ➍
❶ StrKeyDict 是对 UserDict 的扩展。
❷ __missing__ 跟示例 3-7 里的一模一样。
❸ __contains__ 则更简洁些。这里可以放心假设所有已经存储的键都是字符串。因此,只要在 self.data 上查询就好了,并不需要像 StrKeyDict0 那样去麻烦 self.keys()。
❹ __setitem__ 会把所有的键都转换成字符串。由于把具体的实现委托给了 self.data 属性,这个方法写起来也不难。
因为 UserDict 继承的是 MutableMapping,所以 StrKeyDict 里剩下的那些映射类型的方法都是从 UserDict、MutableMapping 和 Mapping 这些超类继承而来的。特别是最后的 Mapping 类,它虽然是一个抽象基类(ABC),但它却提供了好几个实用的方法。以下两个方法值得关注。
MutableMapping.update
这个方法不但可以为我们所直接利用,它还用在 __init__ 里,让构造方法可以利用传入的各种参数(其他映射类型、元素是 (key, value) 对的可迭代对象和键值参数)来新建实例。因为这个方法在背后是用 self[key] = value 来添加新值的,所以它其实是在使用我们的 __setitem__ 方法。
Mapping.get
在 StrKeyDict0(示例 3-7)中,我们不得不改写 get 方法,好让它的表现跟 __getitem__ 一致。而在示例 3-8 中就没这个必要了,因为它继承了 Mapping.get 方法,而 Python 的源码显示,这个方法的实现方式跟 StrKeyDict0.get 是一模一样的。
在写完 StrKeyDict 这个类之后,我读到了 Antonie Pitrou 写的“PEP 455 — Adding a key-transforming dictionary to collections”。文章附带的补丁里包含了一个叫作 TransformDict 的新类型。这个补丁通过 issue 18986 被吸收进了 Python 3.5。7为了试试这个类,我把它提取出来放进了一个单独的模块(在本书代码仓库中:03-dict-set/transformdict.py)。比起 StrKeyDict,TransformDict 的通用性更强,也更复杂,因为它把键存成字符串的同时,还要按照它原来的样子存一份。
7译者浏览 http://bugs.python.org/issue18986后发现这个PEP最终被关闭,相应的补丁也没有被吸收进 Python 3.5。有兴趣的读者可以通过这个链接看看它被拒绝的原因:http://bugs.python.org/issue18986#msg243370 。——译者注
之前我们见识过了不可变的序列类型,那有没有不可变的字典类型呢?这么说吧,在标准库里是没有这样的类型的,但是可以用替身来代替。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论