Python-collections模块
collections模块实现一些特定的数据类型,可以替代Python中常用的内置数据类型如dict, list, set,tuple,简单说就是对基本数据类型做了更上一层的处理。
collections是日常工作中的重点、高频模块,常用类型有:
计数器(Counter)
双向队列(deque)
默认字典(defaultdict)
有序字典(OrderedDict)
具名元组(namedtuple)
1. 计数器Counter
- Counter作为字典dict的一个子类, 可以支持方便、快速的计数 ,将元素进行数量统计,计数后返回一个字典,键值为元素,值为元素个数
常用方法
方法名 描述 most_common(int) 按照元素出现的次数进行从高到低的排序,返回前int个元素的字典 elements 返回经过计算器Counter后的元素,返回的是一个迭代器 update 和set集合的update一样,对集合进行并集更新 subtract 和update类似,只是update是做加法,subtract做减法,从另一个集合中减去本集合的元素 items 返回由Counter生成的字典的所有item keys 返回由Counter生成的字典的所有key values 返回由Counter生成的字典的所有value
示例代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38from collections import Counter
str = "abcabccabde"
li = ['a', 'b', 'a', 'b', 'c']
dic = {'a' : 2, 'b' : 2, 'c' : 3}
# Counter对象获取各个元素的个数,返回字典
print("Counter(str):", Counter(str))
print("Counter(li):", Counter(li))
print("Counter(dic):", Counter(dic))
# most_common(int)按照元素出现的次数进行从高到低的排序,返回前int个元素的元组(键,值)
d1 = Counter(str)
print ("d1.most_common(2):",d1.most_common(2))
d1 = Counter(str)
print ("d1.most_common():",d1.most_common()) # 不加参数返回所有
# elements返回经过计算器Counter后的元素,返回的是一个迭代器
print ("sorted(d1.elements()):", sorted(d1.elements()))
print ("list(d1.elements()):", list(d1.elements()))
# update和set集合的update一样,对集合进行并集更新
print ("d1.update('aaa'):", d1.update("aaa"))
print(d1)
# subtract 和update类似,只是update是做加法,subtract做减法,从另一个集合中减去本集合的元素
print ("d1.substract('aab'):", d1.subtract("aab"))
print(d1)
# items 返回由Counter生成的字典的所有item
print ("d1.items():", d1.items())
# 返回由Counter生成的字典的所有key
print ("d1.keys():", d1.keys())
# 返回由Counter生成的字典的所有value
print ("d1.values():", d1.values())1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21->Counter(str): Counter({'a': 3, 'b': 3, 'c': 3, 'd': 1, 'e': 1})
->Counter(li): Counter({'a': 2, 'b': 2, 'c': 1})
->Counter(dic): Counter({'c': 3, 'a': 2, 'b': 2})
->d1.most_common(2): [('a', 3), ('b', 3)]
->d1.most_common(): [('a', 3), ('b', 3), ('c', 3), ('d', 1), ('e', 1)]
->sorted(d1.elements()): ['a', 'a', 'a', 'b', 'b', 'b', 'c', 'c', 'c', 'd', 'e']
->list(d1.elements()): ['a', 'a', 'a', 'b', 'b', 'b', 'c', 'c', 'c', 'd', 'e']
->d1.update('aaa'): None
->Counter({'a': 6, 'b': 3, 'c': 3, 'd': 1, 'e': 1})
->d1.substract('aab'): None
->Counter({'a': 4, 'c': 3, 'b': 2, 'd': 1, 'e': 1})
->d1.items(): dict_items([('a', 4), ('b', 2), ('c', 3), ('d', 1), ('e', 1)])
->d1.keys(): dict_keys(['a', 'b', 'c', 'd', 'e'])
->d1.values(): dict_values([4, 2, 3, 1, 1])
2.双向队列deque
- deque是栈和队列的一种广义实现,deque是”double-end queue”的简称;deque支持线程安全、有效内存地以近似O(1)的性能在deque的两端插入和删除元素,尽管list也支持相似的操作,但是它主要在固定长度操作上的优化,从而在pop(0)和insert(0,v)(会改变数据的位置和大小)上有O(n)的时间复杂度。
常用方法
方法名 描述 append 队列右边添加元素 appendleft 队列左边添加元素 clear 清空队列中的所有元素 count(value) 返回队列中包含value的个数 extend 队列右边扩展,可以是列表、元组或字典,如果是字典则将字典的key加入到deque extendleft 同extend,在左边扩展 pop 移除并返回队列右边的元素 popleft 移除并返回队列左边的元素 remove(value) 移除队列第一个出现的元素 reverse 队列的所有元素进行反转 rotate(n) 对队列数进行移动
示例代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50from collections import deque
list1 = [0, 1, 2, 3]
dst = deque(list1)
print(dst)
-> deque([0, 1, 2, 3])
# append appendleft
dst.append(4)
dst.appendleft(-1)
print(dst)
->deque([-1, 0, 1, 2, 3, 4])
# extend extendleft
ex = (1, 2)
st = "abcd"
dst.extend(ex)
dst.extendleft(st)
print(dst)
->deque(['d', 'c', 'b', 'a', -1, 0, 1, 2, 3, 4, 1, 2])
# pop popleft
dst.pop()
dst.popleft()
print(dst)
->deque(['c', 'b', 'a', -1, 0, 1, 2, 3, 4, 1])
# count
print(dst.count(2))
->1
# insert
dst.insert(0, '$')
print(dst)
->deque(['$', 'c', 'b', 'a', -1, 0, 1, 2, 3, 4, 1])
# rotate
dst = deque([1, 2, 3, 4, 5], maxlen = 9)
dst.rotate(2)
print(dst)
->deque([4, 5, 1, 2, 3], maxlen=9)
# remove
dst.remove(1)
print(dst)
->deque([4, 5, 2, 3], maxlen=9)
# maxlen
print(dst.maxlen)
->9
3. 默认字典defaultdict
- Python中通过Key访问字典,当Key不存在时,会引发
‘KeyError’
异常。为了避免这种情况的发生,可以使用collections类中的defaultdict()方法来为字典提供默认值
defaultdict是内置数据类型dict的一个子类,基本功能与dict一样,只是重写了一个
missing(key)
和增加了一个可写的对象变量 default_factory如果default_factory属性为None,就报出以key作为遍历的KeyError异常;
如果default_factory不为None,就会向给定的key提供一个默认值,这个值插入到词典中,并返回; 如 d = defaultdict(list) ,默认值是一个空的列表
举例
default_factory属性为None
1
2
3
4# default_factory属性为None
d = defaultdict()
print(d['a'])
# 报错:KeyError: 'a'default_factory属性为list(列表字典)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20# default_factory属性为list
d = defaultdict(list)
print(d['a']) # 不存在键'a'时,返回默认值一个空的列表
print(d) # 此时d中增加了一个有默认值的键了
# ->[]
# ->defaultdict(<class 'list'>, {'a': []})
s = [('y', 1), ('b', 2), ('y', 3), ('b', 4), ('r', 1)]
d = defaultdict(list)
for k, v in s:
d[k].append(v) # 如果不存在键K,则返回一个默认值[]给d[k]
print(d)
# ->defaultdict(<class 'list'>, {'y': [1, 3], 'b': [2, 4], 'r': [1]})
# dict.setdefault()也可以实现相同的功能
e = {}
for k, v in s:
e.setdefault(k, []).append(v)
print(e)
# ->{'y': [1, 3], 'b': [2, 4], 'r': [1]}default_factory属性为set(集合字典)
1
2
3
4
5
6
7# 将default_factory设置为set,使得defaultdict可以建立一个集合字典
s = [('r', 1), ('b', 2), ('r', 3), ('b', 4), ('r', 1), ('b', 4)]
d = defaultdict(set)
for k, v in s:
d[k].add(v) # 键k不存在则返回一个默认的空set,set类似list,但set中元素不能重复
print(d)
# ->defaultdict(<class 'set'>, {'r': {1, 3}, 'b': {2, 4}})default_factory属性为int(计数)
1
2
3
4
5
6
7
8# 设置default_factory为int,使得defaultdict可以用于计数
# 字符串中的字母第一次出现时,字典中没有该字母,default_factory函数调用int()为其提供一个默认值0,加法操作将计算出每个字母出现的次数。
s = 'abidajiadgaji'
d = defaultdict(int)
for k in s:
d[k] += 1
print(d)
# ->defaultdict(<class 'int'>, {'a': 4, 'b': 1, 'i': 3, 'd': 2, 'j': 2, 'g': 1})
4. 具名元组namedtuple
普通元组的局限性: 不能为元组内部的数据进行命名,所以往往我们并不知道一个元组所要表达的意义
namedtuple: 引入了 collections.namedtuple 这个工厂函数,来构造一个带字段名的元组。具名元组的实例和普通元组消耗的内存一样多,因为字段名都被存在对应的类里面。这个类跟普通的对象实例比起来也要小一些,因为 Python 不会用 dict 来存放这些实例的属性。
定义格式:collections.namedtuple(typename, field_names, verbose=False, rename=False)
- typename:元组名称
- field_names: 元组中元素的名称
- rename: 如果元素名称中含有 python 的关键字,则必须设置为 rename=True
- verbose: 默认就好
创建命名元组并实例化
1
2
3
4
5
6
7
8# 创建一个具名元组,需要两个参数,一个是元组名,另一个是元组的各个字段名。
# 后者可以是有多个字符串组成的可迭代对象,或者是有空格分隔开的字段名组成的字符串
User = namedtuple('User', ['name', 'age', 'id'])
User = namedtuple('User', 'name age id')
# User是具名元组的变量名,'User'是具名元组的名称,一般相同
zlx = User('zlx', '22', '123456')
print(zlx)具名元组的特有属性
- 类属性**_fields**: 包含这个类所有字段名的元组
- 类方法 **_make(iterable)**:接受一个可迭代对象来生产这个类的实例
- 实例方法 **_asdict()**:把具名元组以 collections.OrderedDict 的形式返回,可以利用它来把元组里的信息友好的展示出来
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21# 获取所有字段名
print(zlx._fields)
# -> ('name', 'age', 'id')
# 获取各个字段的值
print(zlx.name)
print(zlx.age)
print(zlx.id)
# -> zlx
# -> 22
# -> 123456
# 修改对象属性,注意要使用"_replace"方法
zlx = zlx._replace(age=18)
print(zlx)
# -> User(name='zlx', age=18, id='123456')
# 将User对象转换成字典,注意要使用"_asdict"
print(zlx._asdict())
# -> {'name': 'zlx', 'age': 18, 'id': '123456'}
5. 有序字典 OrderedDict
python中的字典dict是无序的,即迭代遍历(键,值)的顺序可能和插入的顺序不一致,因为它是按照hash来存储的。 常规dict并不跟踪插入顺序,迭代处理会根据键在散列表中存储的顺序来生成值。在OrderDict中则相反,它会记住元素插入的顺序,并在创建迭代器时使用这个顺序。
OrderedDict提供了一个有序的字典结构,内部维护这一个根据键插入顺序排序的双向链表,在迭代操作的时候保持了元素插入时的顺序。需要注意的是,一个OrderedDic的大小是一个普通字典的两倍,因为内部维护了另外一个链表,所有在数据量大的时候,如果不要求保持插入顺序,最好用普通字典。
有序字典和普通字典的相等测试:有序字典是对顺序敏感的,即元素插入顺序不同,两个有序字典就不同;普通字典对顺序是不敏感的,即元素插入顺序不同,两个普通字典仍相同。
有序字典除具有普通字典的原始方法外,还提供了和顺序相关的操作,popitem(last=True)以LIFO方式弹出元素(last=False则以FIFO方式);reversed(dict)返回一个逆序的orderedDict对象
from collections import OrderedDict d = {} d['a'] = 'A' d['b'] = 'B' d['c'] = 'C' for k, v in d.items(): print(k, v) b = {} b['a'] = 'A' b['c'] = 'C' b['b'] = 'B' for k, v in d.items(): print(k, v) print(d == b) # True d = OrderedDict() d['a'] = 'A' d['b'] = 'B' d['c'] = 'C' for k, v in d.items(): print(k, v) b = OrderedDict() b['a'] = 'A' b['c'] = 'C' b['b'] = 'B' for k, v in d.items(): print(k, v) print(d == b) # False print(d.popitem(last=True))