Python的collections模块

在内置数据类型(dict、list、set、tuple)的基础上,collections 模块还提供了几个额外的数据类型:Counter、deque、defaultdict、namedtuple 和 OrderedDict 等。

namedtuple

namedtuple 生成可以使用名字来访问元素内容的 tuple。

我们知道 tuple 可以表示不变集合,例如,一个点的二维坐标就可以表示成:

1
>>> p = (1, 2)

但是,看到 (1, 2),很难看出这个 tuple 是用来表示一个坐标的。这时 namedtuple 就派上了用场:

1
2
3
4
5
6
7
8
9
10
>>> from collections import namedtuple
>>> Point = namedtuple('坐标', ['x', 'y'])
>>> a = Point(1, 2)
>>> a.x
1
>>> a.y
2
>>> a
坐标(x=1, y=2)
>>>

类似的,如果要用坐标和半径表示一个圆,也可以用 namedtuple 定义:

1
2
# namedtuple('名称', [属性list]):
Circle = namedtuple('Circle', ['x', 'y', 'r'])

还可以表示扑克牌的花色和数值:

1
2
3
4
5
6
7
8
9
10
>>> from collections import namedtuple
>>> Card = namedtuple('扑克牌', ['suits', 'number'])
>>> c1 = Card('红桃', '2')
>>> c1
扑克牌(suits='红桃', number='2')
>>> c1.number
'2'
>>> c1.suits
'红桃'
>>>

deque

双端队列,可以快速的从另外一侧追加和推出对象。使用 list 存储数据时,按索引访问元素很快,但是插入和删除元素就很慢了,因为 list 是线性存储,数据量大的时候,插入和删除效率很低。

deque 是为了高效实现插入和删除操作的双向列表,适合用于队列和栈:

1
2
3
4
5
6
>>> from collections import deque
>>> dqls = deque(['a', 'b', 'c'])
>>> dqls.append('x')
>>> dqls.appendleft('y')
>>> dqls
deque(['y', 'a', 'b', 'c', 'x'])

deque 除了实现 list 的 append()pop() 外,还支持 appendleft()popleft(),这样就可以非常高效地往头部添加或删除元素。

OrderedDict

在 Python 3.6 之前使用 dict 时 Key 是无序的,在对 dict 做迭代时,我们无法确定 Key 的顺序。用 OrderedDict 可以保持字典中 Key 的顺序:

1
2
3
4
5
6
7
>>> from collections import OrderedDict
>>> d = {'a': 1, 'b': 2, 'c': 3}
>>> d # dict 的 Key 是无序的
{'a': 1, 'c': 3, 'b': 2}
>>> od = OrderedDict({'a': 1, 'b': 2, 'c': 3})
>>> od # OrderedDict 的 Key 是有序的
OrderedDict([('a', 1), ('b', 2), ('c', 3)])

另外,需要注意的是 OrderedDict 的 Key 会按照插入的顺序排列,不是 Key 本身排序:

1
2
3
4
5
6
>>> od = OrderedDict()
>>> od['z'] = 1
>>> od['y'] = 2
>>> od['x'] = 3
>>> od.keys() # 按照插入的 Key 的顺序返回
['z', 'y', 'x']

注意: 从 Python 3.6 开始,字典的 Key 将会保留插入或声明时候的顺序,但并不是可以被排序的那种顺序。从 3.7 开始这成了正式的 Python 语言特性,3.6 改进的字典更节省内存,运行效率差不多,有一个 “副作用” 就是字典变成有序的了。

defaultdict

defaultdict 是带有默认值的字典。例如有如下值集合:

1
values = [11, 22, 33, 44, 55, 66, 77, 88, 99, 90]

将所有大于 66 的值保存至字典的第一个 key 中,将小于 66 的值保存至第二个 key 的值中。即: {‘k1’: 大于66 , ‘k2’: 小于66}

原生字典的解决方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
values = [11, 22, 33, 44, 55, 66, 77, 88, 99, 90]

my_dict = {}

for value in values:
if value > 66:
if my_dict.has_key('k1'):
# 如果不确定字典是否有对应的 key ,需要做判断
my_dict['k1'].append(value)
else:
my_dict['k1'] = [value]
# 不确定字典中 key 对应的值,还需要声明
else:
if my_dict.has_key('k2'):
my_dict['k2'].append(value)
else:
my_dict['k2'] = [value]

print(my_dict)

defaultdict 字典的解决方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
from collections import defaultdict

values = [11, 22, 33, 44, 55, 66, 77, 88, 99, 90]

my_dict = defaultdict(list)
# 直接创建一个字典,字典中所有的 key 对应的值 value 都是默认的 list

for value in values:
if value>66:
my_dict['k1'].append(value)
# 在不判断 key 是否存在的情况下直接更新数据
else:
my_dict['k2'].append(value)

使用 dict 时,如果引用的 Key 不存在,就会抛出 KeyError 。如果希望 key 不存在时,返回一个默认值,就可以用 defaultdict:

1
2
3
4
5
6
7
>>> from collections import defaultdict
>>> dd = defaultdict(lambda: 'N/A')
>>> dd['key1'] = 'abc'
>>> dd['key1'] # key1存在
'abc'
>>> dd['key2'] # key2不存在,返回默认值
'N/A'

在 defaultdict 中默认值必须是 callable 的,如果想使用不可 callable 的值作为默认值,可以使用不带参数的匿名函数 lambda 解决,因为 lambda 是 callable 的:

1
2
3
4
5
6
7
8
9
10
11
>>> from collections import defaultdict
>>> dd = defaultdict(list)
>>> dd['k']
[]
>>> dd = defaultdict(5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: first argument must be callable or None
>>> dd = defaultdict(lambda : 5)
>>> dd['k']
5

Counter

Counter 类的目的是用来跟踪不可变条目中值出现的次数。它是一个无序的容器类型,以字典的键值对形式存储,其中元素作为key,其计数作为 value。计数值可以是任意的 Interger(包括 0 和负数)。Counter 类和其他语言的 bags 或 multisets 很相似。

1
2
3
>>> c = Counter('abcdeabcdabcaba')
>>> c
Counter({'a': 5, 'b': 4, 'c': 3, 'd': 2, 'e': 1})
有钱任性,请我吃包辣条
0%