collections集合模块
collections
是Python内建的一个集合模块,提供了许多有用的集合类。
namedtuple(具名元组)
namedtuple是一个创建命名元组的函数,也就是创建一个只有类名和属性却不包括方法的简单类,即给元组中的元素命名。
tuple可以表示不变集合,例如,一个点的二维坐标就可以表示成:
p = (1, 2)
但是,看到(1, 2)
,很难看出这个tuple是用来表示一个坐标的。
定义一个class又小题大做了,这时,namedtuple
就派上了用场:
from collections import namedtuple
Point = namedtuple('Point', ['x', 'y']) # 使用列表
p = Point(1, 2)
print(p) # Point(x=1, y=2)
print(p.x, p.y) # 1 2
Person = namedtuple('Person', 'name, age') # 使用字符串,逗号分隔
p = Person('LR', 25)
print(p, p.age) # Person(name='LR', age=25) 25
Score = namedtuple('Score', 'chinese math english') # 使用字符串,空格分隔
s = Score(99, 112, 96)
print(s) # Score(chinese=99, math=112, english=96)
# s = Score(99, 112, 96, 90) # TypeError: __new__() takes 4 positional arguments but 5 were given
namedtuple是一个函数,它用来创建一个自定义的tuple对象,并且规定了tuple元素的个数,并可以用属性而不是索引来引用tuple的某个元素。
这样一来,我们用namedtuple可以很方便地定义一种数据类型,它具备tuple的不变性,又可以根据属性来引用,使用十分方便。
可以验证创建的Point对象是tuple的一种子类:
Score = namedtuple('Score', 'chinese math english') # 使用字符串,空格分隔
s = Score(99, 112, 96)
print(isinstance(s, Score)) # True
print(s.math) # 112
print(isinstance(s, tuple)) # True
print(s[0], s[1]) # 99 112
deque(双向队列)
使用list存储数据时,按索引访问元素很快,但是插入和删除元素就很慢了,因为list是线性存储,数据量大的时候,插入和删除效率很低。
deque是为了高效实现插入和删除操作的双向列表,适合用于队列和栈:
from collections import deque
dq = deque(['a', 'b', 'c'])
dq.append('x')
print(dq) # deque(['a', 'b', 'c', 'x'])
dq.appendleft('y')
print(dq) # deque(['y', 'a', 'b', 'c', 'x'])
dq.pop()
print(dq) # deque(['y', 'a', 'b', 'c'])
dq.popleft()
print(dq) # deque(['a', 'b', 'c'])
有append()
、appendleft()
、pop()
、popleft()
、clear()
、copy()
……这些方法完全不需要死记,只需要明白一点:deque是双向队列,是list的完善,所以列表支持的操作它都支持而且还可以从两端的任意端插入新元素(对xxxleft()方法留个心眼即可)
defaultdict(默认字典)
使用dict时,如果引用的Key不存在,就会抛出KeyError。如果希望key不存在时,返回一个默认值,就可以用defaultdict:
from collections import defaultdict
dd = defaultdict(lambda: 'None')
dd['key1'] = 'abc'
print(dd['key1']) # abc
print(dd['key2']) # None
注意默认值是调用函数返回的,而函数在创建defaultdict对象时传入。
除了在Key不存在时返回默认值,defaultdict
的其他行为跟dict是完全一样的。
OrderedDict(有序字典)
使用dict时,Key是无序的。在对dict做迭代时,我们无法确定Key的顺序。
如果要保持Key的顺序,可以用OrderedDict,它是内置dict的子类(所以也支持一切dict拥有的操作集)
from collections import OrderedDict
d = dict([('a', 1), ('b', 2), ('c', 3)])
print(d) # {'a': 1, 'b': 2, 'c': 3}
od = OrderedDict([('a', 1), ('b', 2), ('c', 3)])
print(od) # OrderedDict([('a', 1), ('b', 2), ('c', 3)])
OrderedDict的Key会按照插入的顺序排列,不是Key本身排序:
od = OrderedDict()
od['b'] = 2
od['a'] = 1
od['c'] = 3
print(od) # OrderedDict([('b', 2), ('a', 1), ('c', 3)])
print(od.keys(), od.values()) # odict_keys(['b', 'a', 'c']) odict_values([2, 1, 3])
print(list(od.keys())) # ['b', 'a', 'c']
OrderedDict可以做成一个FIFO的队列(当元素超过上限时删除最先进入的元素)
class LastUpdateOrderedDict(OrderedDict):
def __init__(self, capacity): # 定义容量capacity
super(LastUpdateOrderedDict, self).__init__()
self._capacity = capacity
def __setitem__(self, key, value):
containsKey = 1 if key in self else 0 # 传入的key若已存在,containsKey为1,若不存在,则containsKey为0
if len(self) - containsKey >= self._capacity: # 当前字典长度减去containskey若大于或等于字典容量,移除第一个key
last = self.popitem(last=False)
print('Remove: ', last)
if containsKey:
del self[key]
print('set: ', (key, value))
else:
print('add: ', (key, value))
OrderedDict.__setitem__(self, key, value)
luod = LastUpdateOrderedDict(3)
luod['a'] = 1
print(luod)
# add: ('a', 1)
# LastUpdateOrderedDict([('a', 1)])
luod['b'] = 2
print(luod)
# add: ('b', 2)
# LastUpdateOrderedDict([('a', 1), ('b', 2)])
luod['c'] = 3
print(luod)
# add: ('c', 3)
# LastUpdateOrderedDict([('a', 1), ('b', 2), ('c', 3)])
luod['d'] = 10
print(luod)
# Remove: ('a', 1)
# add: ('d', 10)
# LastUpdateOrderedDict([('b', 2), ('c', 3), ('d', 10)])
Counter(计数器)
Counter是一个简单的计数器,例如,统计字符出现的个数:
from collections import Counter
# 统计列表中每个元素出现次数
some_data = ['a', '2', 2, 4, 5, '2', 'b', 4, 7, 'a', 5, 'd', 'a', 'z']
print(Counter(some_data)) # Counter({'a': 3, '2': 2, 4: 2, 5: 2, 2: 1, 'b': 1, 7: 1, 'd': 1, 'z': 1})
# 统计字符串中每个字符出现次数
print(list('hello world'))
print(Counter(list('hello world'))) # Counter({'l': 3, 'o': 2, 'h': 1, 'e': 1, ' ': 1, 'w': 1, 'r': 1, 'd': 1})
# 可以动态的使用循环对序列进行计数
ct = Counter()
for i in 'hello world':
ct[i] += 1 # 先是创建一个空的Counter对象,接着对其进行填充
print(ct) # Counter({'l': 3, 'o': 2, 'h': 1, 'e': 1, ' ': 1, 'w': 1, 'r': 1, 'd': 1})
# 也可以直接传入字符串,统计各个字符出现次数
print(Counter('hello world')) # Counter({'l': 3, 'o': 2, 'h': 1, 'e': 1, ' ': 1, 'w': 1, 'r': 1, 'd': 1})