前言
Python 内置模块collections, 目标是提供各种专门的集合数据类型来解决特定的编程问题。
本系列介绍其中的数据结构特点和使用方法, 以便遇到某些特定的问题,可以找到对应的数据来处理, 达到事半功倍的效果
namedtuple | 用于创建其子类的工厂函数tuple ,提供命名字段,允许按名称访问项目,同时保持按索引访问项目的能力 |
deque | 一个类似序列的集合,支持从序列的任一端有效地添加和删除项目 |
⭐OrderedDict | 根据插入键的时间保持键值对排序的字典子类 |
⭐ChainMap | 一个类似字典的类,允许将多个映射视为单个字典对象 |
defaultdict | 一个字典子类,用于为缺失的键构建默认值并自动将它们添加到字典中 |
Counter | 一个字典子类,支持方便地对序列或可迭代中的唯一项进行计数 |
本文介绍其中两个 OrderedDict, ChainMap, 其他的可以参考
Python中的collections模块(一) 使用Counter,pythonic的对象计数方式
Python中的collections模块(三) 命名元组namedtuple和双端队列deque
Python中的collections模块(四) UserString,UserList,和UserDict
1.Python中的dict与OrderedDict
多年来,Python字典都是无序的数据结构。当需要保持字典的内容有序的场景时, 需要一个一种新型字典。即OrderedDict
Python 3.6 及其以后的版本中, 内置dict
类现在也保持其项目的有序性。下面简单介绍一下OrderedDict和dict的区别, 以及何时应该使用OrderedDict.
In [2]: from collections import OrderedDict
In [3]: od = OrderedDict()
In [4]: od['a']=1
In [5]: od['b']=2
In [6]: od['c']=3
# OrderedDict按照内容的添加顺序
In [7]: od
Out[7]: OrderedDict([('a', 1), ('b', 2), ('c', 3)])
In [8]: od2 = OrderedDict(a=1, b=2, c=3)
In [9]: od2
Out[9]: OrderedDict([('a', 1), ('b', 2), ('c', 3)])
在Python3.6 之后,dict和OrderedDict基本相同
In [25]: dct = dict(a=1, b=2, c=3)
In [26]: od = OrderedDict(a=1, b=2, c=3)
# 数据均按被添加的顺寻保持有序
In [27]: dct.items() == od.items()
Out[27]: True
# 均可弹出最后一个item
In [28]: dct.popitem()
Out[28]: ('c', 3)
In [29]: od.popitem()
Out[29]: ('c', 3)
1.1 OrderedDict 特有方法 move_to_end()
OrderedDict和dict的显著区别是,多了一个方法 move_to_end(), 此方法支持将指定item移到字典的末尾或开头,是重新排序字典的神器. move_to_end的两个参数:
-
key
持有标识您要移动的项目的键。如果key
不存在,那么你会得到一个KeyError. -
last
保存一个bool值,该值定义您要将手头的项目移动到字典的哪一端。它默认为True, 移到最后; last=False时, 将item移到ordereddict的最前面.
In [38]: od = OrderedDict(a=1, b=2, c=3, d=4, e=5)
# 将'c' 移到最后
In [39]: od.move_to_end('c')
In [40]: od
Out[40]: OrderedDict([('a', 1), ('b', 2), ('d', 4), ('e', 5), ('c', 3)])
# 将'd' 移到最前
In [41]: od.move_to_end('d', last=False)
In [42]: od
Out[42]: OrderedDict([('d', 4), ('a', 1), ('b', 2), ('e', 5), ('c', 3)])
结论:
- Python < 3.6 使用OrderedDict
- Python > 3.6 dict的顺序已经满足大部分场景, 需要重新排序的时候才考虑使用OrderedDict
2.使用ChainMap管理多个上下文
ChainMap类可以将多个字典组合在一起,使它们在逻辑上显示并表现为一个整体。可以简单理解为高级的dict.update().
2.1 ChainMap与dict.update的区别
例如,我们现在有三个字典数据 dict1, dict2和dict3. 他们的key有交集
经过update之后,会将原数据更新, 如执行dict2.update(dict1)之后会更新dict2的数据, 重复的key的数据会被覆盖.
ChainMap将多个字典逻辑上认为一个整体,内部包含每个字典的引用,并不直接修改源数据. 查找的时候逐层向下查找,知道最后一层还没有的话,抛出KeyError.
2.2 ChainMap使用示例
In [1]: from collections import ChainMap
In [2]: dct1 = {'a': 1, 'b':2, 'c': 3}
In [3]: dct2 = {'c': 30, 'd': 40. 'e': 50}
In [4]: cm = ChainMap(dct1, dct2)
In [5]: cm
Out[5]: ChainMap({'a': 1, 'b': 2, 'c': 3}, {'c': 30, 'd': 40, 'e': 50})
# 读取时从左边第一个字典开始查找
In [6]: cm['c']
Out[6]: 3
In [6]: cm['e']
Out[6]: 50
# 修改只影响左边第一个字典
In [7]: cm['h'] = 66
In [8]: cm['e'] = 77
In [9]: cm
Out[9]: ChainMap({'a': 1, 'b': 2, 'c': 3, 'h': 66, 'e': 77}, {'c': 30, 'd': 40, 'e': 50})
ChainMap提供访问各层数据的方法, 属性maps和 parents
In [10]: cm.maps
Out[10]: [{'a': 1, 'b': 2, 'c': 3, 'h': 66, 'e': 77}, {'c': 30, 'd': 40, 'e': 50}]
In [11]: cm.maps[1]['c']=88
Out[11]: [{'a': 1, 'b': 2, 'c': 3, 'h': 66, 'e': 77}, {'c': 88, 'd': 40, 'e': 50}]
In [12]: cm.parents
Out[12]: ChainMap({'c': 88, 'd': 40, 'e': 50})
In [13]: cm.parents['d']=99
In [14]: cm
Out[14]: ChainMap({'a': 1, 'b': 2, 'c': 3, 'h': 66, 'e': 77}, {'c': 88, 'd': 99, 'e': 50})
new_child()方法, 在ChainMap外层链接新的字典
In [15]: cm.new_child({'x': 'xxx', 'y': 'yyy'})
Out[15]: ChainMap({'x': 'xxx', 'y': 'yyy'}, {'a': 1, 'b': 2, 'c': 3, 'h': 66, 'e': 77}, {'c': 30, 'd': 99, 'e': 50})
ChainMap适合将字典作为一个来管理,可以利用内部映射列表为字典中的重复键定义某种访问优先级。这就是为什么ChainMap
对象非常适合处理多个上下文。
总结
本文介绍了 collections模块中的2中dict的拓展数据类型, OrderedDict和ChainMap.
OrderedDict为字典提供了顺序的功能, 并提高move_to_end方法来改变顺序.
ChainMap提供一个按照上下文使用多个字典中数据的方法.