collections:容器数据类型
文章目录
- 一、ChainMap:搜索多个字典
- 1、访问值:与常规字典相同
- 2、重排:maps属性
- 3、更新值
- (1) ChainMap与原字典相互影响
- (2) new_child() 可以在ChainMap中构建额外字典
- 二、Counter:统计元素出现频次
- 1、初始化:元素序列 / 字典 / 关键字参数
- 2、访问值
- (1) 使用字典 API
- (2) elements():逐一迭代输出元素
- (3) most_common():最常出现的元素
- 3、更新值:update()
- 4、算术与集合操作:加、减、交、并
- 三、defaultdict:对缺失键返回默认值
- 1、setdefault():获取 / 设置键值(dict 内置方法)
- 2、defaultdict:提前设置默认值
- 四、deque:双端队列
- 1、与list相同操作:索引、删除、确定长度
- 2、填充:extend / append / extendleft / appendleft
- 3、消费:pop / popleft
- 4、旋转:rotate 截取续到另一端
- 5、限制队列大小:maxlen 参数
- 五、namedtuple:带命名字段的元组
- 1、创建:namedtuple(类名, 元素信息)
- 2、非法字段名:rename 参数解决冲突
- 3、属性和方法:._fields / _asdict() / _replace()
- 六、OrderedDict:有序字典
- 1、相等性:考虑键值的插入顺序
- 2、重排:move_to_end(键, last)
- 七、collections.abc:容器的抽象基类
collections模块包含除内置类型 list、dict 和 tuple 以外的其他容器数据类型。
import collections
一、ChainMap:搜索多个字典
要点:
ChainMap类
管理一个字典序列,并按照字典传递到构造函数的顺序来查找与键关联的值,支持常规字典的读数方法以及.keys()/.values()/.items()/key in dict
方法。maps
属性中存储的是要搜索的字典列表list
,该列表允许直接更改。ChainMap
实例与原字典相互影响,任何一方发生变化,都会影响另一方的访问值。new_child()
方法可以在ChainMap
实例的最前面构建新字典,也可以向该方法直接传递现有字典。
1、访问值:与常规字典相同
ChainMap
支持与常规字典dict
相同的 API 来访问现有的值,键搜索不到时同样会触发KeyError
。
dict_a = {'a': 'A', 'c': 'C'}
dict_b = {'b': 'B', 'c': 'D'}dict_chain = collections.ChainMap(dict_a, dict_b)# 根据键直接访问值
# 键'c'在字典dict_a、dict_b中都存在,但是字典dict_a先出现,所以输出的是dict_a中对应的值'C'
print('a = ', dict_chain['a'])
print('b = ', dict_chain['b'])
print('c = ', dict_chain['c'])# 调用 .keys()、.values()、.items()方法
print('Keys =', list(dict_chain.keys()))
print('Values =', list(dict_chain.values()))
print('Items:')
for k, v in dict_chain.items():print('{} = {}'.format(k, v))# 判断键是否在字典中
print('"b" in m:', 'b' in dict_chain)
print('"d" in m:', 'd' in dict_chain)
2、重排:maps属性
maps
属性中存储的是要搜索的字典列表list
,可以直接修改该列表来增加新映射,或者改变元素的顺序来调整映射值。
dict_a = {'a': 'A', 'c': 'C'}
dict_b = {'b': 'B', 'c': 'D'}dict_chain = collections.ChainMap(dict_a, dict_b)# list类型,元素是「按照构造顺序排列的字典」
print(dict_chain.maps)# 反转maps列表后,字典dict_b在前,此时键'c'对应的值为'D'
# reversed()函数:返回给定序列的反向迭代器
dict_chain.maps = list(reversed(dict_chain.maps))
print('c = ', dict_chain['c'])
3、更新值
(1) ChainMap与原字典相互影响
如果用来构造ChainMap
的原字典发生了变化,那么所构造的ChainMap
在访问值时也会发生相应的变化。同样,也可以通过为ChainMap
设置值来修改原字典的值。
dict_a = {'a': 'A', 'c': 'C'}
dict_b = {'b': 'B', 'c': 'D'}dict_chain = collections.ChainMap(dict_a, dict_b)# 改变字典dict_a的值,dict_chain也会受到影响
print('Before:', dict_chain['c'])
dict_a['c'] = 'E'
print('After :', dict_chain['c'])# 更新dict_chain中键'c'的值,会影响到字典dict_a
print('Before:', dict_chain)
dict_chain['c'] = 'F'
print('After :', dict_chain)
print('dict_a:', dict_a)
(2) new_child() 可以在ChainMap中构建额外字典
.new_child()
方法可以在maps列表的最前面创建一个新字典,从而来避免修改现有的原字典。这样的好处是,可以很容易地在某次迭代中增加或更新键值,然后在下次迭代中丢弃这些改变。
如果新字典已知或者提前构建,也可以直接向.new_child()
方法传递该字典。
dict_a = {'a': 'A', 'c': 'C'}
dict_b = {'b': 'B', 'c': 'D'}dict_chain1 = collections.ChainMap(dict_a, dict_b)
dict_chain2 = dict_chain1.new_child()# dict_chain2 before: ChainMap({}, {'a': 'A', 'c': 'C'}, {'b': 'B', 'c': 'D'})
print('dict_chain1 before:', dict_chain1)
print('dict_chain2 before:', dict_chain2)dict_chain2['c'] = 'E'# 不会修改字典dict_a和dict_b
# dict_chain2 after: ChainMap({'c': 'E'}, {'a': 'A', 'c': 'C'}, {'b': 'B', 'c': 'D'})
print('dict_chain1 after:', dict_chain1)
print('dict_chain2 after:', dict_chain2)# 新字典已知,直接向new_child()方法传递参数
dict_c = {'c': 'F'}
dict_chain2 = dict_chain1.new_child(dict_c)
print('dict_chain1["c"] = ', dict_chain1['c'])
print('dict_chain2["c"] = ', dict_chain2['c'])
# 等价于下列代码
# 在list前面加星号*,相当于解包(unpack)该列表,将其元素展开为独立的参数或元素
dict_chain2 = collections.ChainMap(dict_c, *dict_chain1.maps)
二、Counter:统计元素出现频次
Counter
是一个容器,用于统计可迭代对象中元素的出现次数,它返回一个类似字典的对象,其中元素是键,计数是值。
1、初始化:元素序列 / 字典 / 关键字参数
Counter
支持3种形式的初始化:
- 提供元素序列
- 提供包含元素键和计数值的字典
- 使用关键字参数将元素名映射到计数值
print(collections.Counter(['a', 'b', 'c', 'a', 'b', 'b']))
print(collections.Counter({'a': 2, 'b': 3, 'c': 1}))
print(collections.Counter(a=2, b=3, c=1))>>> 输出结果:Counter({'b': 3, 'a': 2, 'c': 1})
当然了,也可以使用collections.Counter()
直接构造一个空Counter
。
2、访问值
(1) 使用字典 API
Counter
支持与常规字典dict
相同的 API 来访问现有的值,键搜索不到时不会触发KeyError
,而是返回数字0。
c = collections.Counter('abcdaab')# 结果:3
print(c['a'])
# 结果:0
print(c['f'])
(2) elements():逐一迭代输出元素
elements()
方法返回一个迭代器,该迭代器将生成Counter
知道的所有正计数值元素。
需要注意的是:<1> 元素的输出顺序无法保证;<2> 输出的是元素而不是类别,例如元素a
的计数值是3,那么它将会被输出3次;<3> 不会输出计数值小于等于0的元素。
c = collections.Counter('abdcaab')
c['z'] = 0
print(list(c.elements()))>>> 输出结果:['a', 'a', 'a', 'b', 'b', 'd', 'c']
(3) most_common():最常出现的元素
most_common()
方法用于统计最常出现的 n 个元素及相应计数,返回一个列表list
。如果不向该方法传递参数,则会生成一个由所有元素构成、按照计数值逆序排列的列表,包含非正数计数值的元素。
c = collections.Counter('abdcaabasdnsadfnakscihciecweikfvcweifbacsacoplwffmsdv')
c['z'] = -1# 输出结果:[('a', 8), ('c', 7), ('s', 5)]
print(c.most_common(3))
# 输出结果:[('a', 8), ('c', 7), ('s', 5), ('f', 5), ('d', 4), ('i', 4), ('b', 3), ('e', 3), ('w', 3), ('n', 2), ('k', 2), ('v', 2), ('h', 1), ('o', 1), ('p', 1), ('l', 1), ('m', 1), ('z', -1)]
print(c.most_common())
3、更新值:update()
使用update()
方法可以更新现有Counter
中的元素和计数值,可以向该方法传入序列或字典。与常规字典不同,最终的计数值为传参相应的计数值 + 原来计数值,而非替换。
c = collections.Counter()# 当前结果:Counter({'a': 3, 'b': 2, 'c': 1, 'd': 1})
c.update('abcdaab')
# 当前结果:Counter({'d': 6, 'a': 4, 'b': 2, 'c': 1})
c.update({'a': 1, 'd': 5})
4、算术与集合操作:加、减、交、并
Counter
实例支持用算术和集合操作来完成结果的聚集,也支持+=、-=、&=、|=
等原地执行的操作符。注意,操作完成后,计数值为0或者负数的元素均会被删除。
c1 = collections.Counter(['a', 'b', 'c', 'a', 'b', 'b'])
c2 = collections.Counter('alphabet')add_c = c1 + c2
sub_c = c1 - c2# 交集取计数值中的最小值
inter_c = c1 & c2
# 并集取计数值中的最大值
union_c = c1 | c2
三、defaultdict:对缺失键返回默认值
1、setdefault():获取 / 设置键值(dict 内置方法)
标准字典中包含一个setdefault()
方法,用于安全地「获取或设置」字典的键值。如果键已经存在,则返回对应的值(不会修改字典);如果不存在,则为该键设置默认值,再返回该值。
person = {"name": "Alice", "age": 25}# "name" 已存在,直接返回原值'Alice',字典不会改变
print(person.setdefault("name", "Bob"))
# "gender" 不存在,设置默认值'Female'并返回,字典已经改变
print(person.setdefault("gender", "Female"))
2、defaultdict:提前设置默认值
collections
模块中的defaultdict
可以在初始化时让调用者提前指定默认值,也支持使用关键字参数指定初始存在的映射关系。
def default_factory():return 'default value'# 初始存在的映射关系 {'foo': 'bar'},缺失键的默认取值为 'default value'
d1 = collections.defaultdict(default_factory, foo='bar')
# 缺失键的默认取值为空列表
d2 = collections.defaultdict(list)
四、deque:双端队列
collections
模块中的deque
是一种序列容器,它支持从任意一端增加和删除元素。
1、与list相同操作:索引、删除、确定长度
deque
同样支持list
的一些操作,如索引、确定长度,以及从队列中删除元素。
d = collections.deque('abcdefg')print(len(d))
print(d[0])
print(d[-1])
d.remove('c')
2、填充:extend / append / extendleft / appendleft
可以从任意一端对deque
进行填充:
# 从右端填充
d1 = collections.deque()
d1.extend('abcdefg')
print('extend :', d1)
d1.append('h')
print('append :', d1)# 从左端填充
d2 = collections.deque()
d2.extendleft(range(6)) # deque([5, 4, 3, 2, 1, 0])
d2.appendleft(6)
3、消费:pop / popleft
可以从任意一端对deque
进行消费:
d1 = collections.deque('abcdefg')
d1.pop() # gd2 = collections.deque(range(6))
d2.popleft() # 0
4、旋转:rotate 截取续到另一端
使用rotate
方法,可以将deque
按照任意一个方向旋转:
- 向该方法传递正整数值,会截取尾部一段序列,将其续到头部
- 向该方法传递负整数值,会截取头部一段序列,将其续到尾部
d = collections.deque(range(10))
# 截取尾部两个元素,续到头部:deque([8, 9, 0, 1, 2, 3, 4, 5, 6, 7])
d.rotate(2)d = collections.deque(range(10))
# 截取头部两个元素,续到尾部:deque([2, 3, 4, 5, 6, 7, 8, 9, 0, 1])
d.rotate(-2)
5、限制队列大小:maxlen 参数
配置deque
实例时可以通过maxlen
参数指定队列的最大长度。在队列达到最大长度后,再增加新元素就会删除队列中的现有元素:从左端添加会逐渐删除右端的元素,从右端添加会逐渐删除左端的元素。
d = collections.deque([1, 9, 4], maxlen=3)# deque([9, 4, 2], maxlen=3)
d.append(2)# deque([3, 9, 4], maxlen=3)
d.appendleft(3)
五、namedtuple:带命名字段的元组
标准tuple
使用数值索引来访问其中的元素,collections
模块中的namedtuple
所创建的元组不仅可以使用数值索引,还可以指定名字。
1、创建:namedtuple(类名, 元素信息)
通过namedtuple(类名, 元素名字符串)
可以创建一个元组,这个元组本质上是一个类,因此,既可以使用数值索引来访问元素,也可以使用点记法元组.attr
按照名字访问。
在namedtuple
的传参中:第一个参数是类名;第二个参数中包含了元素名信息,它的形式可以是「以空格或逗号间隔的元素名所构成的的字符串」,也可以是「包含元素名字符串的列表」。
Person = collections.namedtuple('Person', ['name', 'age'])bob = Person(name='Bob', age=30)
print(bob[1]) # 30
jane = Person(name='Jane', age=29)
print(jane.name) # 'Jane'
与常规tuple
类似,namedtuple
也是不可修改的,因此它也可以被用作字典的键或者放在集合中。如果试图通过数字索引来改变值,会触发TypeError
异常;如果试图通过命名属性来改变值,会触发AttributeError
异常。
2、非法字段名:rename 参数解决冲突
字段名重复或者与 Python 关键字冲突,那么就是非法字段名。非法字段名会导致ValueError
异常:
try:collections.namedtuple('Person', 'name class age')
except ValueError as err:# Type names and field names cannot be a keyword: 'class'print(err)try:collections.namedtuple('Person', 'name age age')
except ValueError as err:# Encountered duplicate field name: 'age'print(err)
在创建namedtuple
时把其中的rename
参数设置成True
,就可以对非法字段重命名。非法字段的新名字为:下划线+对应的数字索引。
with_class = collections.namedtuple('Person', 'name class age', rename=True)
# ('name', '_1', 'age')
print(with_class._fields)two_ages = collections.namedtuple('Person', 'name, age,age', rename=True)
# ('name', 'age', '_2')
print(two_ages._fields)
3、属性和方法:._fields / _asdict() / _replace()
如上述代码所示,namedtuple
的字段名保存在._fields
属性中。而使用_asdict()
方法可以将namedtuple
实例转换为OrderedDict
实例,OrderedDict
的键与namedtuple
的字段顺序相同:
Person = collections.namedtuple('Person', 'name age')bob = Person(name='Bob', age=30)
# {'name': 'Bob', 'age': 30}
print(bob._asdict())
_replace()
方法可以替换一些字段的值,并产生一个新实例:
Person = collections.namedtuple('Person', 'name age')bob = Person(name='Bob', age=30)
new = bob._replace(name='Robert')
六、OrderedDict:有序字典
在 Python 3.5及之前,常规的dict
不会记住键的插入顺序。而collections
模块中OrderedDict
会记住向字典中插入键的顺序,并在创建迭代器的时候使用这个顺序。
1、相等性:考虑键值的插入顺序
常规的dict
在检查相等性的时候只会查看其内容,但是OrderedDict
还会考虑键值的插入顺序:
d1 = {}
d1['a'] = 'A'
d1['b'] = 'B'
d1['c'] = 'C'd2 = {}
d2['c'] = 'C'
d2['b'] = 'B'
d2['a'] = 'A'print(d1 == d2) # Trued1 = collections.OrderedDict()
d1['a'] = 'A'
d1['b'] = 'B'
d1['c'] = 'C'd2 = collections.OrderedDict()
d2['c'] = 'C'
d2['b'] = 'B'
d2['a'] = 'A'print(d1 == d2) # False
2、重排:move_to_end(键, last)
使用move_to_end()
方法可以将OrderedDict
中的键移至序列的起始或者末尾位置,来改变键的顺序,last
参数可以控制将元素移至末端(参数值为True
,默认值)还是起始(参数值为False
):
d = collections.OrderedDict([('a', 'A'), ('b', 'B'), ('c', 'C')])# OrderedDict({'a': 'A', 'c': 'C', 'b': 'B'})
d.move_to_end('b')# OrderedDict({'a': 'A', 'c': 'C', 'b': 'B'})
d.move_to_end('b', last=False)
七、collections.abc:容器的抽象基类
collections.abc
模块包含了一些抽象基类,这些基类:
- 为 Python 内置的容器数据结构以及
collections
模块定义的容器数据结构定义了 API; - 可以在调用对象前使用
isinstance()
方法测试某个对象是否支持某个 API; - 有些类提供了方法实现,可以直接继承这些类来构造定制容器类型,这样就不必从头实现每一个方法。
下表展示了这些抽象基类及其用途:
类 | 基类 | API用途 |
---|---|---|
Container | 基本容器特性,如in 操作符 | |
Hashable | 增加了散列支持,可以为容器实例提供散列值 | |
Iterable | 可以在容器内容上创建一个迭代器 | |
Iterator | Iterable | 这是容器内容上的一个迭代器 |
Generator | Iterator | 为迭代器扩展了 PEP 342 的生成器协议 |
Sized | 为知道自己大小的容器增加方法 | |
Callable | 可以作为函数来调用的容器 | |
Sequence | Sized, Iterable, Container | 支持获取单个元素以及迭代和改变元素顺序 |
MutableSequence | Sequence | 支持创建一个实例之后增加和删除元素 |
ByteString | Sequence | 合并 bytes 和 bytearray 的 API |
Set | Sized, Iterable, Container | 支持集合操作,如交集和并集 |
MutableSet | Set | 增加了创建集合后管理集合内容的方法 |
Mapping | Sized, Iterable, Container | 定义dict 使用的只读 API |
MutableMapping | Mapping | 定义创建映射后管理映射内容的方法 |
MappingView | Sized | 定义从迭代器访问映射的视图 API |
ItemsView | MappingView, Set | 视图 API 的一部分 |
KeysView | MappingView, Set | 视图 API 的一部分 |
ValuesView | MappingView | 视图 API 的一部分 |
Awaitable | await 表达式中可用的对象的 API,如协程 | |
Coroutine | Awaitable | 实现协程协议的类的 API |
AsyncIterable | 与async for (PEP 492 中定义)兼容的iterable 的 API | |
AsyncIterator | AsyncIterable | 异步迭代器的 API |