本文通过对官方文档和很多博客的学习总结,详解了colleciotns库各个容器类的使用方法。这个模块实现了针对特定目标的容器,作为Python标准内建容器 dict , list , set , 和 tuple 的替代选择。
本文主要介绍了ChainMap,Counter,deque,defaultdict,namedtuple,OrderedDict。
ChainMap
class collections.ChainMap(*maps)
初识ChainMap
ChainMap类用于将多个字典(映射)快速链接到一起,作为一个单元(逻辑上的字典)处理。通常比创建一个新字典和多次调用update()
实现相同操作要快很多。
例如我们有两个字典a = {'x': 1, 'z': 3}
和b = {'y': 2, 'z': 4}
,需要在这两个字典中执行查找更新等操作,就可以使用ChainMap类将这两个字典链接起来:
a = {'x': 1, 'z': 3}
b = {'y': 2, 'z': 4}
from collections import ChainMap
c = ChainMap(a, b)
print(c['x'], c['y'], c['z']) # 有重复的key时,第一次出现的value会被返回
c['z'] = 5 # 更新c中key对应的value,原来的字典a也会相应的变化。同样更新a的时候c也会变化,这里不再举例
print('c:', c)
print('a:', a)
结果输出:
1 2 3
c: ChainMap({'x': 1, 'z': 5}, {'y': 2, 'z': 4})
a: {'x': 1, 'z': 5}
而如果创建一个新字典并多次调用update()
也可以实现相同操作:
a = {'x': 1, 'z': 3}
b = {'y': 2, 'z': 4}
c = b
c.update(a)
print(c['x'], c['y'], c['z'])
c['z'] = 5 # 更新c中key对应的value,原来的字典a和b都不会相应的变化。同样更新a和b后c不会变化,这里不再举例
print('c:', c)
print('a:', a)
print('b:', b)
结果输出:
1 2 3
c: {'y': 2, 'z': 5, 'x': 1}
a: {'x': 1, 'z': 3}
b: {'y': 2, 'z': 5, 'x': 1}
但这种做法速度更慢,并且更新操作不会在原来的字典和新字典之间相互传递。
ChainMap的实现
在构造函数中创建一个列表maps
,用于按顺序存放传进来的字典,然后重新定义了常见的字典操作方法(如len()
, keys()
, values()
, get()
等)。
例如在上例中,maps
就是maps = [{'x': 1, 'z': 3}, {'y': 2, 'z': 4}]
:
a = {'x': 1, 'z': 3}
b = {'y': 2, 'z': 4}
from collections import ChainMap
c = ChainMap(a, b)
print('maps', c.maps)
print('len(c)', len(c))
print('list(c.keys())', list(c.keys()))
print('list(c.values())', list(c.values()))
print("c.get('x')", c.get('x'))
c['a'] = 0 # 向ChainMap添加新的键值对,会添加到maps[0]中
print(c)
print(a)
结果输出:
maps [{'x': 1, 'z': 3}, {'y': 2, 'z': 4}]
len(c) 3
list(c.keys()) ['y', 'z', 'x']
list(c.values()) [2, 3, 1]
c.get('x') 1
ChainMap({'x': 1, 'z': 3, 'a': 0}, {'y': 2, 'z': 4})
{'x': 1, 'z': 3, 'a': 0}
ChainMap的新功能
除了常见的字典操作,ChainMap还实现了以下方法:
方法 | 作用 |
---|---|
copy() | New ChainMap or subclass with a new copy of maps[0] and refs to maps[1:] |
new_child(m=None) | New ChainMap with a new map followed by all previous maps. If no map is provided, an empty dict is used. |
parents() | New ChainMap from maps[1:] |
pipitem() | 删除并返回maps[0]的最后一个键值对,如果maps[0]是空字典,raise KeyError |
pip(key, *args) | 从maps[0]中删除键值为key的键值对并返回其value,如果key不在maps[0]中,raise KeyError |
clear() | Clear maps[0],将maps[0]变成空列表 |
Counter
class collections.Counter([iterable-or-mapping])
初识Counter
Counter是字典dict的一个子类,用于对可哈希对象进行计数(技术值可为0和负数),例如:
from collections import Counter
test = ['red', 'blue', 'red', 'green', 'blue', 'blue']
cnt = Counter(test)
print(cnt)
print(cnt['red'])
print(cnt['blue'])
print(cnt['green'])
结果输出:
Counter({'blue': 3, 'red': 2, 'green': 1})
2
3
1
Counter的初始化方法
- 直接建立一个空的
Counter
对象,然后手动添加键值对,例如:
输出为:from collections import Counter cnt = Counter() # 建立一个空的Counter对象 # 手动添加 for word in ['red', 'blue', 'red', 'green', 'blue', 'blue']: cnt[word] += 1 print(cnt)
Counter({'blue': 3, 'red': 2, 'green': 1})
- 从
iterable
建立一个Counter
对象,例如:
输出为:from collections import Counter cnt = Counter('gallahad') print(cnt)
Counter({'a': 3, 'l': 2, 'g': 1, 'h': 1, 'd': 1})
- 从一个mapping(字典)建立一个
Counter
对象,例如:
输出为:from collections import Counter cnt = Counter({'red': 4, 'blue': 2}) print(cnt)
Counter({'red': 4, 'blue': 2})
- 从关键字参数新建一个
Counter
对象,例如:
输出为:from collections import Counter cnt = Counter(red=4, blue=2) print(cnt)
Counter({'red': 4, 'blue': 2})
Counter的一些方法
方法 | 功能 |
---|---|
elements() | 返回一个迭代器,其中每个元素将重复出现技术值所指定次。元素会按首次出现的顺序返回。如果一个元素的技术值小于1,elements()会忽略它。 |
most_common([n]) | 返回一个列表,其中包含n个最常见的元素及出现次数(tuple),按常见程度从高到低排序。如果n被被忽略或为None , |
substract([iterable-or-mapping) | 从迭代对象或映射对象减去元素。 |
update([iterable-or-mapping) | 从迭代对象计数元素或者从另一个映射对象(或计数器)添加元素。 |
例如:
from collections import Counter
cnt = Counter(a=4, b=2, c=0, d=-1)
print(list(cnt.elements()))
print(Counter('abracadabra').most_common(3))
tmp = Counter(a=1, b=2, c=3, d=4)
cnt.subtract(tmp)
print(cnt)
cnt.update(tmp)
print(cnt)
输出为:
['a', 'a', 'a', 'a', 'b', 'b']
[('a', 5), ('b', 2), ('r', 2)]
Counter({'a': 3, 'b': 0, 'c': -3, 'd': -5})
Counter({'a': 4, 'b': 2, 'c': 0, 'd': -1})
deque
class collections.deque([iterable[, maxlen]])
初识deque
deque对象是一个双向队列,从iterable创建,如果iterable没有指定,新队列为空。
- deque支持线性安全,内存搞笑添加(append)和弹出(pop),从两端都可以,两个方向的大概开销都是O(1)复杂度;
- list也可以完成类似的操作,但是deque优化了定长操作和
pop(0)
和insert(0, v)
的开销,list开销为O(n),deque开销为O(1); - 如果maxlen没有指定或者为
None
,deque可以增长到任意长度,否则deque就限定到最大长度,一单限定长度的deque满了,当新项加入时,同样数量的项从另一端弹出。
deque的方法和属性
方法 | 功能 |
---|---|
append(x) | 添加x到右端 |
appendleft(x) | 添加x到左端 |
clear() | 移除所有元素,使其长度为0 |
copy() | 创建一份浅拷贝 |
count(x) | 计算deque中元素为x的个数 |
extend(iterable) | 扩展deque的右侧,通过添加iterable中的元素 |
extendleft(iterable) | 扩展deque的左侧,通过添加iterable中的元素。注意,左添加时iterable中的元素的顺序将被反过来添加 |
index(x[, start[, stop]]) | 返回x在deque中的位置(在索引start之后,索引stop之前)。返回第一个匹配项,找不到返回ValueError |
insert(i, x) | 在位置i插入x |
pop() | 移去并且返回deque最右侧的元素,如果没有元素引发一个IndexError |
popleft() | 移去并且返回deque最左侧的元素,如果没有元素引发一个IndexError |
remove(value) | 移除找到的第一个value,如果没有引发一个ValueError |
reverse() | 将deque逆序排列。返回None |
rotate(n=1) | 向右循环移动n步。如果n为负数,就表示向左循环移动 |
maxlen | deque的最大尺寸,没有限定则为None |
除了以上操作,deque 还支持迭代、封存、len(d)
、reversed(d)
、copy.copy(d)
、copy.deepcopy(d)
、成员检测运算符 in
以及下标引用例如通过 d[0]
访问首个元素等。 索引访问在两端的复杂度均为 O(1) 但在中间则会低至 O(n)。 如需快速随机访问,请改用列表。
defaultdict
class collections.defaultdict([default_factory[, ...]])
defaultdict的定义和使用
defaultdict
是内置dict
类的子类。访问普通dict
对象不存在的key
时会返回KeyError
报错,而defaultdict
可以给不存在的key
设置一个默认值,我们需要给参数default_factory
(默认为None
)赋值一个可调用对象(例如函数),当访问不存在的key
时,返回这个可调用对象(不带参数的调用)的返回值,即为默认值,例如:
from collections import defaultdict
# 使用类型函数
print('--------test1----------')
test1 = defaultdict(list) # 这里给defaultdict赋值函数list,函数list()返回值为空列表,故默认值为空列表
print(test1)
print(test1['a'])
print(test1)
test2 = defaultdict(int) # 这里给defaultdict赋值函数int,函数int()返回值为0,故默认值为0
print(test2['a'])
# 使用自定义函数
print('--------test3----------')
def helper():
return "Hello World!"
test3 = defaultdict(helper)
print(test3['a'])
# 使用匿名函数
print('--------test4----------')
test4 = defaultdict(lambda: 1)
print(test4['a'])
输出为:
--------test1----------
defaultdict(<class 'list'>, {})
[]
defaultdict(<class 'list'>, {'a': []})
0
--------test3----------
Hello World!
--------test4----------
1
namedtuple
collections.namedtuple(typename, field_names, *, rename=False, defaults=None, module=None)
初识namedtuple
namedtuple
函数返回一个新的元组子类,名为typename。用法类似于C语言的struct。普通tuple
对象类似于(item0, item1, ..., itemn)
,而namedtuple
返回的元组子类则为每一个item
都命名,既可以像普通tuple
一样通过索引访问item
,也可以通过名字访问item
,例如定义一个点的坐标:
from collections import namedtuple
Point = namedtuple('Point', ['x', 'y', 'z'])
p = Point(11, y=22, z=33) # 两种方式传递参数都可以
print(p[0], p[1], p[2]) # 通过索引访问item
print(p.x, p.y, p.z) # 通过名字访问item
输出为:
11 22 33
11 22 33
namedtuple各个参数的意义和用法
typename
typename是子类的名字,设置为和等号前面的名字一样即可(不同好像也没啥影响)。
field_names
field_names包含各个item的名字,既可以是像['x', 'y', 'z']
这种形式,也可以用空格或都好分隔开,例如'x y z'
或'x, y, z'
。fiel_names包含的名字可以是任意有效的Python标识符,除了下划线开头的以及关键字。
rename
如果rename为真,那么field_names中包含的无效名字会被自动替换为 下划线+位置索引。例如['abc', 'def', 'ghi', 'abc']
中第二个(关键字)和第四个(重复出现)是无效名字,在rename为真时被转换为['abc', '_1', 'ghi', '_3']
。
defaults
defaults表示item的默认值,可以为None
或iterable
。如果defaults指定的默认值个数少于feild_names指定的名字个数,那么默认值赋值给右边的名字。例如field_names为['x', 'y', 'z']
而defaults为(1, 2)
,那么'y'
和'z'
的默认值分别为1和2,我们至少要指定一个'x'
的值。
namedtuple的几个常用方法和属性
_make(iterable)
从给定的iterable创建一个新实例,例如上例中的Point可以如下创建:
t = [11, 22, 33]
p = Point._make(t)
_asdict()
将namedtuple转为字典(namedtuple相对于字典更轻量,占内存小)
print(type(p._asdict())) # 输出为:<class 'dict'>
_replace(**kwargs)
返回一个新namedtuple,并将指定域替换为新的值:
print(p._replace(x=44)) # 输出为:Point(x=44, y=22, z=33)
_fields
字符串元组,列出了各个item的名字。
print(p._fields) # 输出为:('x', 'y', 'z')
OrderedDict
class collections.OrderdDict([items])
以前python中的字典dict
是无序的,即插入的顺序和输出的顺序不一定是相同的,因为它是按照hash来存储的。
而OrderedDict是有序的,插入的顺序和输出的顺序相同,并且两个OrderedDict对象,插入元素的顺序不同,即使元素相同,它们也不相等。
但目前dict
已经具备了记忆插入顺序的能力,OrderedDict已经不那么重要了,一些细微差别这里不再介绍。