有时候为了方便起见,就算某个键在映射里不存在,我们也希望在通过这个键读取值的时候能得到一个默认值。有两个途径能帮我们达到这个目的,一个是通过 defaultdict 这个类型而不是普通的 dict,另一个是给自己定义一个 dict 的子类,然后在子类中实现 __missing__ 方法。下面将介绍这两种方法。
3.4.1 defaultdict:处理找不到的键的一个选择
示例 3-5 在 collections.defaultdict 的帮助下优雅地解决了示例 3-4 里的问题。在用户创建 defaultdict 对象的时候,就需要给它配置一个为找不到的键创造默认值的方法。
具体而言,在实例化一个 defaultdict 的时候,需要给构造方法提供一个可调用对象,这个可调用对象会在 __getitem__ 碰到找不到的键的时候被调用,让 __getitem__ 返回某种默认值。
比如,我们新建了这样一个字典:dd = defaultdict(list),如果键'new-key' 在 dd 中还不存在的话,表达式 dd['new-key'] 会按照以下的步骤来行事。
(1) 调用 list() 来建立一个新列表。
(2) 把这个新列表作为值,'new-key' 作为它的键,放到 dd 中。
(3) 返回这个列表的引用。
而这个用来生成默认值的可调用对象存放在名为 default_factory 的实例属性里。
示例 3-5 index_default.py:利用 defaultdict 实例而不是setdefault 方法
"""创建一个从单词到其出现情况的映射""" import sys import re import collections WORD_RE = re.compile(r'\w+') index = collections.defaultdict(list) with open('test.txt', encoding='utf-8') as fp: for line_no, line in enumerate(fp, 1): for match in WORD_RE.finditer(line): word = match.group() colunm_no = match.start() + 1 location = (line_no, colunm_no) index[word].append(location) # 以字母顺序打印结果 for word in sorted(index, key=str.upper): print(word, index[word])
把 list 构造方法作为 default_factory 来创建一个defaultdict。
如果 index 并没有 word 的记录,那么 default_factory 会被调用,为查询不到的键创造一个值。这个值在这里是一个空的列表,然后这个空列表被赋值给 index[word],继而被当作返回值返回,因此.append(location) 操作总能成功。
如果在创建 defaultdict 的存在的键会触发 KeyErr。
defaultdict 里的 default_factory 只会在__getitem__ 里被调用,在其他的方法里完全不会发挥作用。比如,dd 是个 defaultdict,k 是个找不到的键, dd[k] 这个表达式会调用 default_factory 创造某个默认值,而 dd.get(k) 则会返回 None。
所有这一切背后的功臣其实是特殊方法 __missing__。它会在defaultdict 遇到找不到的键的时候调用 default_factory,而实际上这个特性是所有映射类型都可以选择去支持的。