collections的常用类型有:
计数器(Counter)
双向队列(deque)
默认字典(defaultdict)
有序字典(OrderedDict)
可命名元组(namedtuple)
使用以上类型时需要导入模块 from collections import *
1. Counter
Counter 作为字典(dict)的一个子类用来进行hashtable计数,将元素进行数量统计、计数后返回一个字典,键值为元素:值为元素个数
- s = 'abcbcaccbbad'
- l = ['a','b','c','c','a','b','b']
- d = {'2': 3, '3': 2, '17': 2}
- # Counter 获取各元素的个数,返回字典
- print(Counter(s)) # Counter({'c': 4, 'b': 4, 'a': 3})
- print(Counter(l)) # Counter({'b': 3, 'a': 2, 'c': 2})
- print(Counter(d)) # Counter({3: 3, 2: 2, 17: 1})
most_common
- # most_common(int) 按照元素出现的次数进行从高到低的排序,返回前int个元素的字典
- m1 = Counter(s)
- print(m1) # Counter({'c': 4, 'b': 4, 'a': 3, 'd': 1})
- print(m1.most_common(3)) # [('c', 4), ('b', 4), ('a', 3)]
elements
- # elements 返回经过计数器Counter后的元素,返回的是一个迭代器
- e1 = Counter(s)
- print(''.join(sorted(e1.elements()))) # aaabbbbcccc
- e2 = Counter(d)
- print(sorted(e2.elements())) # ['17', '17', '2', '2', '2', '3', '3'] 字典返回value个key
update
- # update 和set集合的update一样,对集合进行并集更新
- u1 = Counter(s)
- u1.update('123a')
- print(u1) # Counter({'a': 4, 'c': 4, 'b': 4, '1': 1, '3': 1, '2': 1})
substract
- # substract 和update类似,只是update是做加法,substract做减法,从另一个集合中减去本集合的元素,
- sub1 = 'which'
- sub2 = 'whatw'
- subset = Counter(sub1)
- print(subset) # Counter({'h': 2, 'i': 1, 'c': 1, 'w': 1})
- subset.subtract(Counter(sub2))
- print(subset) # Counter({'c': 1, 'i': 1, 'h': 1, 'a': -1, 't': -1, 'w': -1}) sub1中的h变为2,sub2中h为1,减完以后为1
iteritems
iterkeys
itervalues
2.deque
appendleft
- dq = deque('abc123')
- dq.append('right')
- dq.appendleft('left')
- print(dq) # deque(['left', 'a', 'b', 'c', '1', '2', '3', 'right'])
clear
clear 清空队列中的所有元素count
extend
extendleft
- dq = deque('abc123')
- dq.extend({1:10,2:20})
- dq.extendleft('L')
- print(dq) # deque(['L', 'a', 'b', 'c', '1', '2', '3', 1, 2])
pop
popleft
popleft 移除并且返回队列左边的元素remove
reverse
reverse 队列的所有元素进行反转rotate
rotate(n) 对队列的数进行移动,若n<0,则往左移动即将左边的第一个移动到最后,移动n次,n>0 往右移动- dq = deque([1,2,3,4,5])
- dq.rotate(-1) # 左移,1往左移动一位到5后面
- print(dq)
3.defaultdict
- dic = collections.defaultdict(dict)
- dic['k1'].update({'k2':'aaa'})
- print(dic)
我看看一下传统的字典类型
- b = dict()
- b['k1'].append('2')
- # TypeError: 'type' object is not iterable
4.OrderedDict
OrderDict 叫做有序字典,也是字典类型(dict)的一个子类,是对字典的一个补充。 前面我们说过,字典类型是一个无序的集合,如果要想将一个传统的字典类型进行排序一般会怎么做了,我们可能会将字典的键值取出来做排序后在根据键值来进行有序的输出,我们看下面的一个例子:- # 定义传统字典
- dic1 = dict()
- # 按顺序添加字典内容
- dic1['a'] = '123'
- dic1['b'] = 'jjj'
- dic1['c'] = '394'
- dic1['d'] = '999'
- print(dic1) # 结果: {'a': '123', 'c': '394', 'b': 'jjj', 'd': '999'}
- # 排序
- dic1_key_list = []
- for k in dic1.keys():
- dic1_key_list.append(k)
- dic1_key_list.sort()
- for key in dic1_key_list:
- print('dic1字典排序结果 %s:%s' %(key,dic1[key]))
以上为定义传统字典类型时的一个简单排序过程。 如果我们定义一个有序字典时,将不用再如此麻烦, 字典顺序将按照录入顺序进行排序且不会改变。
- # 定义有序字典
- dic2 = OrderedDict()
- dic2['a'] = '123'
- dic2['b'] = 'jjj'
- dic2['c'] = 'abc'
- dic2['d'] = '999'
- for k, v in dic2.iteritems():
- print('有序字典:%s:%s' %(k,v))
5.nametuple
标准的tuple类型使用数字索引来访问元素,
- bob = ('Bob', 30, 'male')
- print('Representation:', bob)
- jane = ('Jane', 29, 'female')
- print('\nField by index:', jane[0])
- print('\nFields by index:')
- for p in [bob, jane]:
- print('%s is a %d year old %s' % p)
这种对于标准的元组访问,我们需要知道元素对应下标索引值,但当元组的元素很多时,我们可能无法知道每个元素的具体索引值,这个时候就是可命名元组登场的时候了。
nametuple 的创建是由自己的类工厂nametuple()进行创建,而不是由标准的元组来进行实例化,通过nametuple()创建类的参数包括类名称和一个包含元素名称的字符串
- from collections import namedtuple
- #创建一个nametuplede 类,类名称为Person,并赋给变量P
- P = namedtuple('Person', 'name,age,gender')
- print('Type of Person:', type(P)) # Type of Person: <class 'type'>
- #通过Person类实例化一个对象bob
- bob = P(name='Bob', age=30, gender='male')
- print('\nRepresentation:', bob) # Representation: Person(name='Bob', age=30, gender='male')
- #通过Person类实例化一个对象jane
- jane = P(name='Jane', age=29, gender='female')
- print('\nField by name:', jane.name) # Field by name: Jane
- print('\nFields by index:')
- for p in [bob, jane]:
- print('%s is a %d year old %s' % p)
- # Fields by index:
- # Bob is a 30 year old male
- # Jane is a 29 year old female
通过上面的实例可以看出,我们通过nametuple()创建了一个Person的类,并复制给P变量,Person的类成员包括name,age,gender,并且顺序已经定了,在实例化zhangsan这个对象的时候,对张三的属性进行了定义。这样我们在访问zhangsan这个元组的时候就可以通过张三的属性来复制(zhangsan.name、zhangsan.age等)。这样就算这个元组有1000个元素我们都能通过元素的名称来访问而不用考虑元素的下标索引值。
非法的参数值
使用nametuple()来创建类的时候,传递的成员属性参数名称不能非法(不能为系统参数名称),且参数名称不能重复,否则会报值错误
- # 参数字段的名称非法,包含系统名称class
- try:
- p = namedtuple('Person','age,name,class,gender')
- print(p._fields)
- except ValueError as err:
- print(err)
- # Type names and field names cannot be a keyword: 'class'
- # 类成员字段参数名称重复 age
- try:
- p1 = namedtuple('Person','age,gender,name,age')
- print(p1._fields)
- except ValueError as err:
- print(err)
- # Encountered duplicate field name: 'age'
但是也有时候我们是无法控制的,如果参数的名称来自外部,比如是通过读取数据库中的内容来传递的参数,此时我们无法手工的修改参数名称,那该如何是好呢! 别担心,只需要增加一个属性就OK了,它就是rename
- # 参数字段的名称非法,包含系统名称class
- try:
- p = namedtuple('Person','age,name,class,gender',rename=True)
- print(p._fields)
- except ValueError as err:
- print(err)
- # ('age', 'name', '_2', 'gender')
- # 类成员字段参数名称重复 age
- try:
- p1 = namedtuple('Person','age,gender,name,age',rename=True)
- print(p1._fields)
- except ValueError as err:
- print(err)
- # ('age', 'gender', 'name', '_3')
从以上的实例我们看出,当有参数错误的时候,系统自动将错误的参数通过增加 "下划线+参数索引" 的方式自动将参数名称替换了。
ollections是Python内建的一个集合模块,提供了许多有用的集合类。
namedtuple
我们知道tuple
可以表示不变集合,例如,一个点的二维坐标就可以表示成:
>>> p = (1, 2)
但是,看到(1, 2)
,很难看出这个tuple
是用来表示一个坐标的。
定义一个class又小题大做了,这时,namedtuple
就派上了用场:
>>> from collections import namedtuple
>>> Point = namedtuple('Point', ['x', 'y'])
>>> p = Point(1, 2)
>>> p.x
1
>>> p.y
2
namedtuple
是一个函数,它用来创建一个自定义的tuple
对象,并且规定了tuple
元素的个数,并可以用属性而不是索引来引用tuple
的某个元素。
这样一来,我们用namedtuple
可以很方便地定义一种数据类型,它具备tuple的不变性,又可以根据属性来引用,使用十分方便。
可以验证创建的Point
对象是tuple
的一种子类:
>>> isinstance(p, Point)
True
>>> isinstance(p, tuple)
True
类似的,如果要用坐标和半径表示一个圆,也可以用namedtuple
定义:
# namedtuple('名称', [属性list]):
Circle = namedtuple('Circle', ['x', 'y', 'r'])
deque
使用list
存储数据时,按索引访问元素很快,但是插入和删除元素就很慢了,因为list
是线性存储,数据量大的时候,插入和删除效率很低。
deque是为了高效实现插入和删除操作的双向列表,适合用于队列和栈:
>>> from collections import deque
>>> q = deque(['a', 'b', 'c'])
>>> q.append('x')
>>> q.appendleft('y')
>>> q
deque(['y', 'a', 'b', 'c', 'x'])
deque
除了实现list的append()
和pop()
外,还支持appendleft()
和popleft()
,这样就可以非常高效地往头部添加或删除元素。
defaultdict
使用dict
时,如果引用的Key不存在,就会抛出KeyError
。如果希望key不存在时,返回一个默认值,就可以用defaultdict
:
>>> from collections import defaultdict
>>> dd = defaultdict(lambda: 'N/A')
>>> dd['key1'] = 'abc'
>>> dd['key1'] # key1存在
'abc'
>>> dd['key2'] # key2不存在,返回默认值
'N/A'
注意默认值是调用函数返回的,而函数在创建defaultdict
对象时传入。
除了在Key不存在时返回默认值,defaultdict
的其他行为跟dict
是完全一样的。
OrderedDict
使用dict
时,Key是无序的。在对dict
做迭代时,我们无法确定Key的顺序。
如果要保持Key的顺序,可以用OrderedDict
:
>>> from collections import OrderedDict
>>> d = dict([('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本身排序:
>>> od = OrderedDict()
>>> od['z'] = 1
>>> od['y'] = 2
>>> od['x'] = 3
>>> list(od.keys()) # 按照插入的Key的顺序返回
['z', 'y', 'x']
OrderedDict
可以实现一个FIFO(先进先出)的dict,当容量超出限制时,先删除最早添加的Key:
from collections import OrderedDict
class LastUpdatedOrderedDict(OrderedDict):
def __init__(self, capacity):
super(LastUpdatedOrderedDict, self).__init__()
self._capacity = capacity
def __setitem__(self, key, value):
containsKey = 1 if key in self else 0
if len(self) - containsKey >= self._capacity:
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)
Counter
Counter
是一个简单的计数器,例如,统计字符出现的个数:
>>> from collections import Counter
>>> c = Counter()
>>> for ch in 'programming':
... c[ch] = c[ch] + 1
...
>>> c
Counter({'g': 2, 'm': 2, 'r': 2, 'a': 1, 'i': 1, 'o': 1, 'n': 1, 'p': 1})
Counter
实际上也是dict
的一个子类,上面的结果可以看出,字符'g'
、'm'
、'r'
各出现了两次,其他字符各出现了一次。
小结
collections
模块提供了一些有用的集合类,可以根据需要选用。