这一节总结了标准库里
collections
模块中,除了defaultdict
之外的不同映射类型。
OrderedDict
OrderedDict
类型在添加键的时候会保持顺序,因此键的迭代次序总是一致的。OrderedDict
的 popitem
方法默认删除并返回的是字典里的最后一个元素,但是如果像 my_odict.popitem(last=False)
这样调用它,那么它删除并返回第一个被添加进去的元素。
Counter
Counter
类型会给“键”准备一个整数计数器。每次更新一个“键”的时候都会增加这个计数器。所以这个类型可以用来给可散列表对象计数,或者是当成多重集来用——多重集合就是集合里的元素可以出现不止一次。Counter
实现了 +
和 -
运算符用来合并记录,还有像 most_common([n])
这类很有用的方法。most_common([n])
会按照次序返回映射里最常见的 n
个键和它们的计数。
import collections
# 以元素作为 key,以 key 出现的次数来作为 value
st = collections.Counter('abcacababca')
print(st) # Counter({'a': 5, 'b': 3, 'c': 3})
# 以元素作为 key,以 key 出现的次数来作为 value
lt = collections.Counter(['a', 'b', 'c', 'a', 'c', 'a', 'b', 'a', 'b', 'c', 'a'])
print(lt) # Counter({'a': 5, 'b': 3, 'c': 3})
# 更新键计数器
st.update('aaaazzzz')
print(st) # Counter({'a': 9, 'z': 4, 'b': 3, 'c': 3})
# list 可以和 str 一起使用
lt.update('aaaazzzz')
print(lt) # Counter({'a': 9, 'z': 4, 'b': 3, 'c': 3})
# 合并两个计数器
zt = st + lt
print(zt) # Counter({'a': 18, 'z': 8, 'b': 6, 'c': 6})
# 返回映射里最常见的 n 个 key 和出现的次数;
z = zt.most_common(2)
print(z) # [('a', 18), ('z', 8)]
ChainMap
ChainMap
类型可以容纳数个不同的映射对象,然后在进行键查找操作的时候,这些对象会被当作一个整体被逐个查找,直到键被找到为止。这个功能在给有嵌套作用域的语言做解释器的时候很有用,可以用一个映射对象来代表一个作用域的上下文。在 collections
文档介绍 ChainMap
对象的那一部分里有一些具体的使用示例,其中包含了下面这个 Python 变量查询规则的代码片段:
import builtins
pylookup = ChainMap(locals(), globals(), vars(builtins))
UserDict
这个类其实就是把标准 dict
用纯 Python 又实现了一遍。跟 OrderedDict
和 Counter
这些开箱即用的类型不同,UserDict
是让用户继承写子类的。更倾向于从 UserDict
而不是从 dict
继承的主要原因是,后者有时会在某些方法的实现上走一些捷径,导致我们不得不在它的子类中重写这些方法,但是 UserDict
就不会带来这些问题。
示例 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