映射的弹性键查询

有时候为了方便起见,就算某个键在映射里不存在,我们也希望在通过这个键读取值的时候能得到一个默认值。有两个途径能帮我们达到这个目的,一个是通过 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,而实际上这个特性是所有映射类型都可以选择去支持的。

 

上一篇:defaultdict


下一篇:如何在Python 3.7中对Counter / defaultdict进行排序?