Python入门之collections模块
1.简介
collections 是 python 的内置模块,提供了很多方便且高性能的关于集合的操作,掌握这些知识有助于提高代码的性能和可读性。
2.常用功能
1、命名元组 (namedtuple)
Python 中提供了基础的不可变数据结构元组tuple
,对元组元素的访问需通过索引来完成,对此需要熟记每个下标对应的具体含义。如果元素数量一多,要记清楚这些东西就会比较麻烦了,于是就出现了命名元组namedtuple
。
创建命名元组
命名元组的构造函数接受两个参数:
typename
:元组的名字;field_names
:元组各个元素的名称,也就是属性名称。
比如:
collections.namedtuple("Point",["x","y"])
这样就创建了一个叫做Point
的命名元组类,它拥有两个属性x
,y
。
第二个参数["x","y"]
也可以写成"x y"
或者"x,y"
,即用空格或者逗号隔开属性名,即:
collections.namedtuple("Point","x y")
collections.namedtuple("Point","x,y")
当然,元素数量是无限制的,可以自定义,之后就可以将构造函数调用,从而快速建立一个元组,调用方式可以是将其赋值给一个变量:
Point = collections.namedtuple("Point","x,y")
p = collections.namedtuple("Point","x,y") #变量名不一定要和第一个参数相同
以上得到的变量Point
或者p
并不直接是一个元组对象,它只是一个类,如果要创建它的实例,则需要像创建类实例一样调用它,需要传入对应数量的参数才行,不然会报错:
p = Point(x = 0, y = 0)
这样就创建了一个实例p
,他的内容分别是x = 0,y = 0
访问命名元组的元素
通过collections.namedtuple
创建的命名元组类,实际上是元组类的子类,因此命名元组也可以通过索引访问元素:
print(p1[0])
print(p1[1])
得到的结果: 0
0
当然,命名元组也可以通过属性访问:
print(p.x)
print(p.y)
得到的结果: 1
1
修改元素
我们都知道,元组的元素是不容许修改的,在这里如果需要修改元组的元素,也不能简单的使用p.x = 1
,需要调用成员函数_replace()
,它会返回一个包含新值的新实例,比如:
p = p._replace(x = 1) #将p1的x值从0换到1
这样子就不会有问题了,我们也实现了某种意义上的修改元组的元素。
2、计数器(Counter)
计数器是一个无序容器,用于记录各种值出现的次数。它采用键值对的形式存储,要记录的值作为key
,这个值出现的次数作为value
,value
值可正可负。collectis模块提供了计数器的创建函数,我们可以通过调用直接创造一个无参构造函数。
创建计数器
c = collections.Counter()
这样就创建了一个空的计数器实例c
。
也可以从list
,tuple
,dict
,字符串等**可迭代对象(iterable)**创建:
c = collections.Counter(['a','a','b','b','c']) #从list创建
c = collections.Counter(('a','a','b','b','c')) #从tuple创建
c = collections.Counter({'a':2,'b':2,'c':1}) #从dict创建
c = collections.Counter("aabbc") #从字符串创建
上面四条语句创建出来的计数器c
都是相同的: {'a': 2, 'b': 2, 'c': 1}
最后,你也可以直接指定键值对,来创建计数器:
c = collections.Counter(a=2,b=2,c=1)
创建出来的计数器c
,与上面四条语句创建的计数器c
是相同的。 #####访问元素 计数器是dict
的子类,因此可以像使用dict
那样访问计数器元素:
print(c['a'])
print(c['b'])
print(c.c)
得到的结果是: 2
2
1
不过与dict
不同的是,当访问计数器中不存在的元素的时候,不会产生异常,而是返回0
,比如:
print(c['d']) #c中没有d元素,但不会发生异常
上面的代码能够正常运行,并且结果是:0
增加计数与减少计数
要改变计数器中某一元素的值,除了可以使用操作dict
的方式:c.a = XXX
外,计数器还提供了两个成员函数update
和subt\fract
。
update
函数接受一个可迭代对象,然后将这个对象中各种值出现的次数加到计数器中,比如:
c.update("aabbcd") #字符串也是可迭代对象
上面这行语句执行后,c
的值从原来的: {'a': 2, 'b': 2, 'c': 1}
增加到了: {'a': 4, 'b': 4, 'c': 2, 'd': 1}
subtract
函数与update
函数类似,不过它是从计数器中减去可迭代对象中各种值出现的次数,比如:
c.subtract("aabbcd")
上面这行语句执行后,c
的值从原来的: {'a': 4, 'b': 4, 'c': 2, 'd': 1}
减少到了: {'a': 2, 'b': 2, 'c': 1, 'd': 0}
删除元素
从上面的例子可以发现,当计数器中一个元素的值减少到0
时,它并不会自动从计数器中删除,如果要删除元素,可以使用del
函数:
del(c['d'])
上面这行语句执行后,c
的值从原来的: {'a': 2, 'b': 2, 'c': 1, 'd': 0}
变成了: {'a': 2, 'b': 2, 'c': 1}
即元素d
被删除了。
TopN操作
计数器还提供了一个获取出现次数最多的n
个元素的成员函数most_common
,它返回包含这些元素的list
,比如:
top1 = c.most_common(1) #出现次数最多的元素
print(top1)
top2 = c.most_common(2) #出现次数最多的两个元素
print(top2)
all = c.most_common() #不提供参数则返回所有元素
print(all)
得到的结果是: [('a', 2)]
[('a', 2), ('b', 2)]
[('a', 2), ('b', 2), ('c', 1)]
注意:如果有多个元素的值相同,那么它们之间的顺序是不可确定的,不要在它们的顺序上作任何假设。
双向队列 (deque)
双向队列是一种能在队列两端都进行入队、出队操作的数据结构,比普通的队列更加灵活也更加复杂。
创建双向队列
就像计数器Counter
,双向队列可以调用无参构造函数创建一个空队列,也可以使用可迭代对象创建,并初始化一个队列,比如:
d = collections.deque() #创建一个空队列
d = collections.deque(['a','b','c']) #从list创建
d = collections.deque(('a','b','c')) #从tuple创建
d = collections.deque({'a':0,'b':1,'c':2}) #从dict创建
d = collections.deque("abc") #从字符串创建
第一行语句创建一个空队列,下面四行语句创建了含有元素a
,b
,c
的队列,要注意当从dict
创建时,使用的是它的键key
,而不是值value
。
队列操作
双向队列与list
类似,也有append
和pop
这两个成员函数,他们的作用分别是向队列的右边增加元素和从队列的右边删除并返回一个元素,比如:
d.append('d') #向右边增加一个元素'd'
print(d)
d.pop() #从右边删除一个元素
print(d)
得到的结果: deque(['a', 'b', 'c', 'd'])
deque(['a', 'b', 'c'])
与append
,pop
相对应的,还有一组对队列左边进行操作的函数:appendleft
,popleft
,用法也与前面的一组类似:
d.appendleft('+') #向左边增加一个元素'+'
print(d)
d.popleft() #从左边删除一个元素
print(d)
得到的结果: deque(['+', 'a', 'b', 'c'])
deque(['a', 'b', 'c'])
双向队列还提供了一对操作:extend
和extendleft
,用于将一个可迭代对象的所有迭代值,依次加入到队列的右边或者左边:
d1 = collections.deque()
d1.extend("123")
print(d1)
d1 = collections.deque()
d1.extendleft("123")
print(d1)
得到的结果是: deque(['1', '2', '3'])
deque(['3', '2', '1'])
可以注意到,上下两个结果的值的顺序是相反的。
需要注意的是这两种方式都只接收迭代式,若要一次只加入一个元素,还需要用到append()方法
比如:
编程要求
根据提示,补充右侧编辑器Begin-End
区间代码,完成函数功能,函数读取输入的一个数字n(n≥0)
,然后输出一个有n
个元素,元素从0
到n-1
的“震荡”的队列,“震荡”的定义见测试说明。
当输入:5
时,输出的“震荡”队列应该是: deque([3, 1, 0, 2, 4])
即以0为中心,奇数在左,偶数在右
首先就会想到左右添加的操作,但用extend()和extendleft()编写后发现程序报错,他们只接受可迭代的序列,如果是单个元素,还需要使用append()和appendleft()
有序字典 (OrderedDict)
有序字典和普通的dict
基本上是相似的,只有一点不同,那就是有序字典中键值对的顺序会保留插入时的顺序。
创建有序字典
有序字典的创建方法和普通的dict
类似,不过由于多了保留顺序的功能,因此在使用可迭代对象创建有序字典时,可以对它先排个序,让创建出来的字典元素也是有序的:
data = [('a',1),('b',3),('c',2)]
od = collections.OrderedDict(sorted(data,key=lambda s:s[0]))#按数据中key值的大小排序
print(od)
od = collections.OrderedDict(sorted(data,key=lambda s:s[1]))#按数据中value值的大小排序
print(od)
得到的结果: OrderedDict([('a', 1), ('b', 3), ('c', 2)])
OrderedDict([('a', 1), ('c', 2), ('b', 3)])
这里使用的sorted
函数,它返回对一个可迭代对象排序后的结果,如果可迭代对象的元素不能直接进行比较(比如元素是一个list
或tuple
等),则需要指定key
函数。
这里使用lambda表达式lambda s:s[0]
和lambda s:s[1]
,分别指定key
为data
中每个元素(tuple类型)的第一个元素和第二个元素。
修改顺序
有序字典提供了一个move_to_end
函数,这个函数可以将指定的键值对移动到最前面或者最后面,即最左边或最右边:
dt = collections.OrderedDict()
dt['a'] = 0
dt['b'] = 1
dt['c'] = 2
dt.move_to_end('b',last = False) #将`b`键值对移动到最前方
print(dt)
dt.move_to_end('b',last = True) #将`b`键值对移动到最后方
print(dt)
得到的结果: OrderedDict([('b', 1), ('a', 0), ('c', 2)])
OrderedDict([('a', 0), ('c', 2), ('b', 1)])