官方文档:https://yiyibooks.cn/xx/python_352/library/collections.html
参考:
https://blog.csdn.net/songfreeman/article/details/50502194
https://www.cnblogs.com/Eva-J/articles/7228075.html#_label15
Collections模块
在内置数据类型(dict、list、set、tuple)的基础上,collections模块还提供了几个额外的数据类型:Counter、deque、defaultdict、namedtuple和OrderedDict等。
1.namedtuple: 生成可以使用名字来访问元素内容的tuple
2.deque: 双端队列,可以快速的从另外一侧追加和推出对象
3.Counter: 计数器,主要用来计数
4.OrderedDict: 有序字典
5.defaultdict: 带有默认值的字典
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
与字典dict的items类似,返回由Counter生成的字典的所有item,只是在Counter中此方法返回的是一个迭代器,而不是列表
iterkeys
与字典dict的keys方法类似,返回由Counter生成的字典的所有key,只是在Counter中此方法返回的是一个迭代器,而不是列表
itervalues
与字典dict的values方法类似,返回由Counter生成的字典的所有value,只是在Counter中此方法返回的是一个迭代器,而不是列表
2.deque
deque 包含在文件_collections.py中,属于高性能的数据结构(High performance data structures)之一.可以从两端添加和删除元素,常用的结构是它的简化版。
deque常用方法:
deque
str1 = 'abc123cd' dq = deque(str1) print(dq) # deque(['a', 'b', 'c', '1', '2', '3', 'c', 'd'])
append
队列右边添加元素
appendleft
队列左边添加元素
dq = deque('abc123') dq.append('right') dq.appendleft('left') print(dq) # deque(['left', 'a', 'b', 'c', '1', '2', '3', 'right'])
clear
clear 清空队列中的所有元素
count
count(value) 返回队列中包含value的个数,结果类型为 integer
extend
extend 队列右边扩展,可以是列表、元组或字典,如果是字典则将字典的key加入到deque
extendleft
extendleft 同extend, 在左边扩展
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
pop 移除并且返回队列右边的元素
popleft
popleft 移除并且返回队列左边的元素
remove
remove(value) 移除队列第一个出现的元素(从左往右开始的第一次出现的元素value)
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)
我们看上面的例子,字典dic在定义的时候就定义好了值为字典类型,虽然现在字典中还没有键值 k1,但仍然可以执行字典的update方法. 这种操作方式在传统的字典类型中是无法实现的,必须赋值以后才能进行值得更新操作,否则会报错。
我看看一下传统的字典类型
b = dict() b['k1'].append('2') # TypeError: 'type' object is not iterable
注意:初始化时,指定的类型一般是方法不是实例!!!
# 正确的 dic1 = collections.defaultdict(dict) dic2 = collections.defaultdict(list) dic3 = collections.defaultdict(lambda: 'N/A')
# 错误的 dic4 = collections.defaultdict(list())
另一个注意点:当从字典中获取一个未定义 或 不存在的key时,value会以默认值给出!不会报错!!!
>>> from collections import defaultdict >>> dd = defaultdict(lambda: 'N/A') >>> dd['key1'] = 'abc' >>> dd['key1'] # key1存在 'abc' >>> dd['key2'] # key2不存在,返回默认值 'N/A'
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))
其他对比示例:
示例: import collections print "Regular dictionary" d={} d['a']='A' d['b']='B' d['c']='C' for k,v in d.items(): print k,v print " Order dictionary" d1 = collections.OrderedDict() d1['a'] = 'A' d1['b'] = 'B' d1['c'] = 'C' d1['1'] = '1' d1['2'] = '2' for k,v in d1.items(): print k,v 输出: Regular dictionary a A c C b B Order dictionary a A b B c C 1 1 2 2 # 可以看到,同样是保存了ABC等几个元素,但是使用OrderedDict会根据放入元素的先后顺序进行排序。所以输出的值是排好序的。 # OrderedDict对象的字典对象,如果其顺序不同那么Python也会把他们当做是两个不同的对象,请看示例: print 'Regular dictionary:' d2={} d2['a']='A' d2['b']='B' d2['c']='C' d3={} d3['c']='C' d3['a']='A' d3['b']='B' print d2 == d3 print ' OrderedDict:' d4=collections.OrderedDict() d4['a']='A' d4['b']='B' d4['c']='C' d5=collections.OrderedDict() d5['c']='C' d5['a']='A' d5['b']='B' print d1==d2 输出: Regular dictionary: True OrderedDict: False # 有序排序: dd = {'banana': 3, 'apple':4, 'pear': 1, 'orange': 2} #按key排序 kd = collections.OrderedDict(sorted(dd.items(), key=lambda t: t[0])) print kd #按照value排序 vd = collections.OrderedDict(sorted(dd.items(),key=lambda t:t[1])) print vd #输出 OrderedDict([('apple', 4), ('banana', 3), ('orange', 2), ('pear', 1)]) OrderedDict([('pear', 1), ('orange', 2), ('banana', 3), ('apple', 4)])
5.namedtuple
标准的tuple类型使用数字索引来访问元素,
bob = ('Bob', 30, 'male') print('Representation:', bob) jane = ('Jane', 29, 'female') print(' Field by index:', jane[0]) print(' Fields by index:') for p in [bob, jane]: print('%s is a %d year old %s' % p)
这种对于标准的元组访问,我们需要知道元素对应下标索引值,但当元组的元素很多时,我们可能无法知道每个元素的具体索引值,这个时候就是可命名元组登场的时候了。
namedtuple 的创建是由自己的类工厂namedtuple()进行创建,而不是由标准的元组来进行实例化,通过namedtuple()创建类的参数包括类名称和一个包含元素名称的字符串
from collections import namedtuple #创建一个namedtuple 类,类名称为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(' Representation:', bob) # Representation: Person(name='Bob', age=30, gender='male') #通过Person类实例化一个对象jane jane = P(name='Jane', age=29, gender='female') print(' Field by name:', jane.name) # Field by name: Jane print(' Fields 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
通过上面的实例可以看出,我们通过namedtuple()创建了一个Person的类,并复制给P变量,Person的类成员包括name,age,gender,并且顺序已经定了,在实例化zhangsan这个对象的时候,对张三的属性进行了定义。这样我们在访问zhangsan这个元组的时候就可以通过张三的属性来复制(zhangsan.name、zhangsan.age等)。这样就算这个元组有1000个元素我们都能通过元素的名称来访问而不用考虑元素的下标索引值。
非法的参数值
使用namedtuple()来创建类的时候,传递的成员属性参数名称不能非法(不能为系统参数名称),且参数名称不能重复,否则会报值错误
# 参数字段的名称非法,包含系统名称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')
从以上的实例我们看出,当有参数错误的时候,系统自动将错误的参数通过增加 "下划线+参数索引" 的方式自动将参数名称替换了。
关于 namedtuple 的个人总结
# namedtuple 总结 from collections import namedtuple # 创建有两种方式:列表 or 有空格的字符串 Website = namedtuple('Website', ['name', 'url', 'founder']) Website = namedtuple('Website', 'name url founder') # 赋值有两种方式: # 1、可迭代对象,元组 or 列表 w = ('Sohu', 'http://www.google.com/', u'张朝阳') l = ['Sina', 'http://www.sina.com.cn/', '王志东'] # 以元组形式赋值 w1 = Website._make(w) Website(name='Sohu', url='http://www.google.com/', founder='张朝阳') # 以列表形式赋值 w2 = Website._make(l) Website(name='Sina', url='http://www.sina.com.cn/', founder='王志东') # 除了上边这样,我们还可以直接赋值(执行一个函数),但我总感觉是 类的实例化 w3 = Website(name='163', url='http://www.163.com/', founder='丁磊') (input) Website(name='163', url='http://www.163.com/', founder='丁磊') (output) # 关于点 . 操作 # 我们可以通过 . 的方式获取数据 当然索引的方式也是可以的 eg: print(w1.name, w1.url, w1.founder) Sohu http://www.google.com/ 张朝阳 print(w1[0], w1[1], w1[2]) Sohu http://www.google.com/ 张朝阳 # 那这样 通过索引都能获取数据,我们可以尝试另一种方式 print('%s %s %s'%w1) Sohu http://www.google.com/ 张朝阳 # 看到这里,我理解的是:其实这个方法(函数)实际上丢回的接口还是一个元组,只是有对应的枚举获取方式 # namedtuple子类不仅可以使用item的index访问item,还可以通过item的name进行访问。可以将namedtuple理解为c中的struct结构,其首先将各个item命名, 然后对每个item赋予数据。