虽然 Python 提供了一些简单的数据结构类型,如 list 、 tuple 、 set 和 dict;这些数据类型可以满足我们日常使用,但 collections 就是为了代替这些标准数据类型而来。
快速查看表
collections 提供了 8 个 对象和 1 个工厂函数
对象
功能
deque
类似列表(list)的容器,实现了在两端快速添加(append)和弹出(pop)
ChainMap
类似字典(dict)的容器类,将多个映射集合到一个视图里面
Counter
字典的子类,提供了可哈希对象的计数功能
OrderedDict
字典的子类,保存了他们被添加的顺序
UserDict
封装了字典对象,简化了字典子类化
UserList
封装了列表对象,简化了列表子类化
UserString
封装了列表对象,简化了字符串子类化
namedtuple()
创建命名元组子类的工厂函数
defaultdict
字典的子类,提供了一个工厂函数,为字典查询提供一个默认值
namedtuple()
语法:namedtuple(typename, field_names, *, rename=False, defaults=None, module=None)
namedtuple() 是一个工厂函数,它返回一个名为 typename 的元组类对象(元组的子类,因为它继承自tuple)
部分源码
result = type(typename, (tuple,), class_namespace)
field_names 是一个列表,表示这个类对象的字段名(参数名);
rename 表示无效参数名会自动转换成位置参数,转换格式为 “_” + 位置索引;
部分源码
if rename:
seen = set()
for index, name in enumerate(field_names):
if (not name.isidentifier()
or _iskeyword(name)
or name.startswith('_')
or name in seen):
field_names[index] = f'_{index}'
seen.add(name)
defaults 是为 field_names 提供默认值,其类型为一个可迭代对象。(注意:defaults 匹配最右边的参数,如参数 ['x', 'y', 'z'] 和默认值 (1, 2),y 默认值 1, z 默认值 2)
module 如果有值,会覆盖类对象的 __module__ 属性。
使用这个元组类对象来创建对象,可以通过字段名来获取属性值,也可以通过索引和迭代获取值。
官方提供的示例代码:
>>> Point = namedtuple('Point', ['x', 'y'])
>>> Point.__doc__ # 类对象的文档字符串
'Point(x, y)'
>>> p = Point(11, y=22) # 用位置参数或关键字实例化
>>> p[0] + p[1] # 像普通元组一样可索引
33
>>> x, y = p # 像普通的元组一样解包
>>> x, y
(11, 22)
>>> p.x + p.y # 按名称访问字段
33
>>> d = p._asdict() # 转换为字典
>>> d['x']
11
>>> Point(**d) # 从字典转换
Point(x=11, y=22)
>>> p._replace(x=100) # _replace() 类似于str.replace(),但以命名参数为目标
Point(x=100, y=22)
defaultdict
defaultdict 返回一个包含默认值的字典对象
defaultdict 接收一个参数 default_factory,它是 defaultdict 对象的一个属性;构造时,第一个参数为该属性提供初始值,默认为 None。
dict 在默认情况下是没有默认值的,当我们获取不存在的 key 时会引发 KeyError ,通过 setdefault 、 get 方法可以为获取不存在的 key 时设置默认值,但 defaultdict 能更简单高效的处理默认值的问题。
创建带有默认值的字典:
In [1]: from collections import defaultdict
In [2]: d = defaultdict(int, {'a': 1, 'b': 2})
In [3]: d['a']
Out[3]: 1
In [4]: d['b']
Out[4]: 2
In [5]: d['c']
Out[5]: 0
统计问题:
s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
d = defaultdict(list)
for k, v in s:
d[k].append(v)
r = sorted(d.items())
print(r) # [('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])]
deque (double-ended queue)
语法: deque([iterable[, maxlen]])
deque 是一个双端队列,它是具有队列和栈的性质的数据结构。
iterable 参数是一个可迭代对象,maxlen 用来限制队列的最大长度。
如果没有 deque 我们一般使用 list 作为队列和栈使用,使用 pop 弹出元素,使用 append 增加元素。
官方文档是这样表述的:
虽然 list 对象也支持类似操作,不过这里优化了定长操作和 pop(0) 和 insert(0, v) 的开销。它们引起 O(n) 内存移动的操作,改变底层数据表达的大小和位置。
deque 支持 list 所有操作并额外添加了以下内容:
appendleft(x)
添加 x 到左端。
extendleft(iterable)
扩展deque的左侧,通过添加iterable参数中的元素。注意,左添加时,在结果中iterable参数中的顺序将被反过来添加。
popleft()
移去并且返回一个元素,deque 最左侧的那一个。 如果没有元素的话,就引发 IndexError。
rotate(n=1)
向右循环移动 n 步。 如果 n 是负数,就向左循环。
如果deque不是空的,向右循环移动一步就等价于 d.appendleft(d.pop()) , 向左循环一步就等价于 d.append(d.popleft()) 。
In [1]: from collections import deque
In [2]: d = deque('abcd')
In [3]: d
Out[3]: deque(['a', 'b', 'c', 'd'])
In [4]: d.popleft()
Out[4]: 'a'
In [5]: d.pop()
Out[5]: 'd'
In [6]: d.extendleft('efg')
In [7]: d
Out[7]: deque(['g', 'f', 'e', 'b', 'c'])
In [8]: d.rotate()
In [9]: d
Out[9]: deque(['c', 'g', 'f', 'e', 'b'])
In [10]: d.rotate(-1)
In [11]: d
Out[11]: deque(['g', 'f', 'e', 'b', 'c'])
ChainMap
官方文档解释:
一个 ChainMap 类是为了将多个映射快速的链接到一起,这样它们就可以作为一个单元处理。它通常比创建一个新字典和多次调用 update() 要快很多。
这个类可以用于模拟嵌套作用域,并且在模版化的时候比较有用。
一个ChainMap将多个dict(或其他映射)组合在一起。来创建一个单一的、可更新的视图。
In [1]: from collections import ChainMap
In [2]: cm = ChainMap({'a': 'A', 'b': 'B'}, {'c': 'C', 'b': 'BB'})
In [3]: cm
Out[3]: ChainMap({'a': 'A', 'b': 'B'}, {'c': 'C', 'b': 'BB'})
In [4]: list(cm)
Out[4]: ['c', 'b', 'a']
In [5]: cm['b']
Out[5]: 'B'
In [6]: cm['b'] = 'BBB'
In [7]: cm
Out[7]: ChainMap({'a': 'A', 'b': 'BBB'}, {'c': 'C', 'b': 'BB'})
In [8]: cm.maps
Out[8]: [{'a': 'A', 'b': 'BBB'}, {'c': 'C', 'b': 'BB'}]
基本的映射存储在一个列表中。 该列表可以使用maps属性进行访问或更新。
查找连续搜索底层映射,直到找到一个键。相反,写入、更新和删除只对第一个映射进行操作。
部分源码
def __getitem__(self, key):
for mapping in self.maps:
try:
return mapping[key] # can't use 'key in mapping' with defaultdict
except KeyError:
pass
return self.__missing__(key) # support subclasses that define __missing__
def __setitem__(self, key, value):
self.maps[0][key] = value
def __delitem__(self, key):
try:
del self.maps[0][key]
except KeyError:
raise KeyError('Key not found in the first mapping: {!r}'.format(key))
def pop(self, key, *args):
try:
return self.maps[0].pop(key, *args)
except KeyError:
raise KeyError('Key not found in the first mapping: {!r}'.format(key))
ChainMap 支持 dict 所有的方法;此外,ChainMap 还包含 一个可以更新的映射列表 maps ,上文提到过。还有 new_child ,它返回一个新的 ChainMap,它有一个参数 m ,如果 m 有内容,返回的 ChainMap 会在映射列表前加上 m;否则加上一个空字典。
In [1]: from collections import ChainMap
In [2]: cm = ChainMap({'a': 'A', 'b': 'B'}, {'c': 'C', 'b': 'BB'})
In [3]: n_cm = cm.new_child()
In [4]: n_cm
Out[4]: ChainMap({}, {'a': 'A', 'b': 'B'}, {'c': 'C', 'b': 'BB'})
In [5]: n_cm = cm.new_child({'e': 'E', 'f': 'F'})
In [6]: n_cm
Out[6]: ChainMap({'e': 'E', 'f': 'F'}, {'a': 'A', 'b': 'B'}, {'c': 'C', 'b': 'BB'})
部分源码
def new_child(self, m=None):
if m is None:
m = {}
return self.__class__(m, *self.maps)
以及 parents ,它返回一个除了映射列表中第一个字典以外的所有字典的映射。
In [1]: from collections import ChainMap
In [2]: cm = ChainMap({'a': 'A', 'b': 'B'}, {'c': 'C', 'b': 'BB'})
In [3]: cm.parents
Out[3]: ChainMap({'c': 'C', 'b': 'BB'})
他与 ChainMap(*cm.maps[1:]) 相同。
部分源码
@property
def parents(self):
return self.__class__(*self.maps[1:])
Counter
Counter 是一个计数器,它接收的参数是可迭代对象或映射对象,当然也可以是 Counter 对象,只要是可哈希对象就可以被计数。 Counter 是 dict 的子类。
In [1]: from collections import Counter
In [2]: Counter('abcdaccd')
Out[2]: Counter({'a': 2, 'b': 1, 'c': 3, 'd': 2})
In [3]: Counter(['hey', 'hello', 'hey'])
Out[3]: Counter({'hey': 2, 'hello': 1})
Counter 支持字典的所有方法并且还提供了3个方法:
elements()
返回一个迭代器,每个元素重复的次数与它的计数次数相同。 元素会按首次出现的顺序返回。 如果一个元素的计数值小于一,此元素则会被忽略。
In [1]: from collections import Counter
In [2]: c = Counter('abcdaccd')
In [3]: c.elements()
Out[3]:
In [4]: sorted(c.elements())
Out[4]: ['a', 'a', 'b', 'c', 'c', 'c', 'd', 'd']
most_common([n])
返回一个列表,列出 n 个最常见的元素和它们的数量,从最常见到最少。 如果 n 为 None,则列出所有元素的数量。计数值相等的元素按首次出现的顺序排序。
In [1]: from collections import Counter
In [2]: c = Counter('abcdaccd')
In [3]: c.most_common()
Out[3]: [('c', 3), ('a', 2), ('d', 2), ('b', 1)]
In [4]: c.most_common(2)
Out[4]: [('c', 3), ('a', 2)]
subtract([iterable-or-mapping])
从迭代对象或映射对象减去元素。和dict.update()一样,但是减去计数而不是替换它们。计数可以减到零以下。 输入和输出都允许包含0和负数。
In [1]: from collections import Counter
In [2]: c = Counter('abcdaccd')
In [3]: c
Out[3]: Counter({'a': 2, 'b': 1, 'c': 3, 'd': 2})
In [4]: c.subtract('cd')
In [5]: c
Out[5]: Counter({'a': 2, 'b': 1, 'c': 2, 'd': 1})
值得注意的是 fromkeys 并未实现,原因在源码是这样注释的:
对于计数器来说,没有等价的方法,因为在Counter.fromkeys('aaabbc', v=2)这样的情况下,语义会很模糊。
初始化计数器为零值没有必要,因为零已经是计数器查找的默认值。 使用Counter(set(iterable))可以很容易地将其初始化为1。 对于更奇特的情况,先用字典理解或dict.fromkeys()创建一个字典。
另外update 的工作方式与字典略有不同,像 dict.update() 一样,但是添加计数而不是替换它们。
update 参数可以是一个迭代,一个字典,或者另一个Counter实例。
关于 Counter 的骚操作:
位运算联合(|)是取任一 Counter 计数器的最大值。
位运算相交(&)是对应 Counter 计数的最小值。
计数器减法,但只保留正数的结果。
计数器加法,但只保留正数的结果。
...
In [1]: from collections import Counter
In [2]: c = Counter('abcdaccd')
In [3]: cc = Counter('abdd')
In [4]: cc & c
Out[4]: Counter({'a': 1, 'b': 1, 'd': 2})
In [5]: cc | c
Out[5]: Counter({'a': 2, 'b': 1, 'd': 2, 'c': 3})
In [6]: c - cc
Out[6]: Counter({'a': 1, 'c': 3})
In [7]: c + cc
Out[7]: Counter({'a': 3, 'b': 2, 'c': 3, 'd': 4})
OrderedDict
顾名思义,有顺序的字典。它是 dict 的子类,OrderedDict 插入时记录字典的顺序。
popitem(last=True)
从字典中删除并返回一个 (key,value) 键值对。如果 last 为 True,则按 LIFO 顺序返回键值对,如果 False 则按 FIFO 顺序返回键值对。
move_to_end(key, last=True)
将一个现有的元素 key 移动到末尾(如果 last 为 False,则移动到开头)。如果元素不存在,则引发KeyError。
OrderedDict 还可以通过 reversed() 来逆序迭代,迭代对象每次 yield OrderedDict 对象的 key。
In [1]: from collections import OrderedDict
In [2]: od = OrderedDict({'a': 'A', 'b': 'B', 'c': 'C'})
In [3]: od
Out[3]: OrderedDict([('a', 'A'), ('b', 'B'), ('c', 'C')])
In [4]: od.popitem()
Out[4]: ('c', 'C')
In [5]: od.move_to_end('a')
In [6]: od
Out[6]: OrderedDict([('b', 'B'), ('a', 'A')])
In [7]: r_od = reversed(od)
In [8]: r_od
Out[8]:
In [9]: list(r_od)
Out[9]: ['a', 'b']
额外的东西
一般使用以上数据类型已足以,但总有特殊清空下需要自定义类型,collections 提供了更容易处理的类,方便你基础它来构造自己的类型,而不是直接使用继承底层的数据结构。
UserDict
UserDict 类是用作字典对象的外包装。
UserList
这个类封装了列表对象。
UserString
UserString 类是用作字符串对象的外包装。