Python 字典和集合(子类化UserDict)

本章内容的大纲如下:
常见的字典方法
如何处理查找不到的键
标准库中 dict 类型的变种set 和 frozenset 类型
散列表的工作原理
散列表带来的潜在影响(什么样的数据类型可作为键、不可预知的
顺序,等等)

子类化UserDict

就创造自定义映射类型来说,以 UserDict 为基类,总比以普通的
dict 为基类要来得方便。

这体现在,我们能够改进示例 3-7 中定义的 StrKeyDict0 类,使得所
有的键都存储为字符串类型。

而更倾向于从 UserDict 而不是从 dict 继承的主要原因是,后者有时
会在某些方法的实现上走一些捷径,导致我们不得不在它的子类中重写
这些方法,但是 UserDict 就不会带来这些问题。

另外一个值得注意的地方是,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 的源码
(https://hg.python.org/cpython/file/3.4/Lib/_collections_abc.py#l422)显示,这个方法的实现方式跟 StrKeyDict0.get 是一模一样的。

在写完 StrKeyDict 这个类之后,我读到了 Antonie Pitrou 写
的“PEP 455 — Adding a key-transforming dictionary to
collections”(https://www.python.org/dev/peps/pep-0455/)。文章附
带的补丁里包含了一个叫作 TransformDict 的新类型。这个补丁
通过 issue 18986(http://bugs.python.org/issue18986)被吸收进了
Python 3.5。为了试试这个类,我把它提取出来放进了一个单独的模
块(在本书代码仓库中:03-dictset/
transformdict.py,https://github.com/fluentpython/examplecode/
blob/master/03-dict-set/transformdict.py)。比起
StrKeyDict,TransformDict 的通用性更强,也更复杂,因为它
把键存成字符串的同时,还要按照它原来的样子存一份。

之前我们见识过了不可变的序列类型,那有没有不可变的字典类型呢?
这么说吧,在标准库里是没有这样的类型的,但是可以用替身来代替。

猜你喜欢

转载自blog.csdn.net/weixin_42291376/article/details/147079903
今日推荐