10.8 模块:collections
简介
collections是Python内建的一个集合模块,其中提供了许多有用的集合类。
注意:使用这些类的时候要先从collections模块导入
from collections import XXX
namedtuple
一个点的二维坐标可以表示为:
p=(1,2)
但是,看到(1,2),很难看出这个tuple是用来表示一个坐标的。
定义一个class又小题大做了,这时,namedtuple就派上了用场:
from collections import namedtuple Point=namedtuple('Point',['x','y']) p=Point(1,3) print (p.x,p.y) 1 3
namedtuple是一个函数,它用来创建一个自定义的tuple对象,并且规定了tuple元素的个数,并且可以用属性而不是索引来引用tuple的某个元素。
这样,我们用namedtuple定义了一种数据类型,它具备tuple的不变性,又可以根据属性来引用,使用十分方便。
简单理解,namedtuple函数创建了一个继承自tuple的子类,该子类的名字即为namedtuple赋值号左边的那个(如上文的Point = namedtuple(...)的左边的Point),该子类的属性确定,且名字为namedtuple第二个参数List的中的字符串元素。但是通过属性来引用时,如p.x,不带字符串引号。
可以验证Point是tuple的子类
isinstance(p,Point)
True
isinstance(p,tuple)
True
类似的,要用圆心坐标和半径表示一个圆:
Circle=namedtuple('Circle',['x','y','r']) c=Circle(1,2,3)#圆心(1,2) 半径3的圆
补充:namedtuple的第一个参数应该为类名称,但是实际上,该项取值可以随意,创建对象时,只用到了namedtuple左边那个名称(如上文的Circle = namedtuple(...)的Circle)。
deque
使用List存储数据时,按索引访问元素很快,但是插入和删除元素就很慢了,因为List是线性存储(类似数组),数据量大的时候,插入和删除效率很低。
deque是为了实现高效插入和删除的双向List,适合用于队列和栈
用法和List很类似,除了实现了List的append()、pop()外,还支持appendleft()与popleft()。这样可以很高效地往头部添加或删除元素。
q=deque(['a','b','c']) q.append('x') q.appendleft('y') q deque(['y', 'a', 'b', 'c', 'x'])
defaultdict
使用dict时,如果引用的Key不存在,就会抛出KeyError。如果希望Key不存在时,返回一个默认值,就可以用defaultdict:
dd=defaultdict(lambda:'N/A') dd['Key1']='abc' dd['Key1'] 'abc' dd['Key2'] 'N/A'
注意:默认值是调用函数返回的,而函数在创建defaultdict对象时作为参数传入。
除了在Key不存在时返回默认值,defaultdict的其他行为和dict是完全一样的。
OrderedDict
使用dict时,Key是无序的。在对dict做迭代时,我们无法确定Key的顺序(注意是保持Key创建时候的次序,而非大小顺序)。
如果要保持Key的顺序(注意①是Key而不是Value②是创建次序而非大小顺序),可以用OrderedDict:
from collections import OrderedDict d=dict(a=1,c=3,b=2) od=OrderedDict(a=1,c=3,b=2) od OrderedDict([('a', 1), ('c', 3), ('b', 2)])
list(od.keys()) ['a', 'c', 'b']
ChainMap
ChaimMap可以把一组dict串起来并组成一个逻辑上的dict。ChainMap本身也是一个dict,但查找的时候,会按照顺序在内部的dict依次查找。
什么时候使用ChainMap最合适?举个例子:应用程序往往都需要传入参数,参数可以通过①命令行传入,可以通过②环境变量传入,还可以有③默认参数。我们可以用ChainMap实现参数的优先级查找,即先查命令行参数,如果没有传入,再查环境变量,最后再用默认参数。
下面的代码演示了如何查找user和color两个参数:
#构造命令行参数: parser=argparse.ArgumentParser() parser.add_argument('-u','--user') parser.add_argument('-c','--color') namespace=parser.parse_args() command_line_args={k:v for k,v in vars(namespace).items() if v} #组合构成ChainMap combined=ChainMap(command_line_args,os.environ,defaults) print('color=%s'%combined['color']) print('user=%s'%combined['user'])
补充:
ChianMap的作用是对多个字典dict进行链接,如上文combined=ChainMap(command_line_args,os.environ,defaults)。把字典command_line_args、os.environ、defaults三个dict对象绑定在一起变成了一个所有字典总和的大字典,但这不等于字典的简单拼接!与普通字典拼接最大的区别在于该大字典中能够保存一样的建Key!当你用被重复的Key去访问Value的时候,得到的一定是在大字典里位置靠前的Value,被链接起来的字典会因为排序的不同而产生优先级。
Counter
Counter是一个简单的计数器
from collections import Counter c=Counter() for ch in 'programming': c[ch]+=1 c Counter({'r': 2, 'g': 2, 'm': 2, 'p': 1, 'o': 1, 'a': 1, 'i': 1, 'n': 1}) c.update('hello')#用'hello'更新c c Counter({'r': 2, 'o': 2, 'g': 2, 'm': 2, 'l': 2, 'p': 1, 'a': 1, 'i': 1, 'n': 1, 'h': 1, 'e': 1})
总结:
一共讲了五种collections模块的类:
namedtuple、deque、defaultdict、Ordereddict、ChainMap
实际上可以用的类不止这五种。
namedtuple:
实际上定义了一种只有属性的简易类,该类继承自tuple
例一:坐标类
Point = namedtuple('Point' , ['x','y'])
该类的名字为namedtuple赋值号左边的那个(比如Point),属性为namedtuple第二个参数list中的。
from collections import namedtuple Point = namedtuple('P',['x','y']) Circle = namedtuple('C' , ['x' , 'y' , 'r']) P=Point(1,2) P.x 1 C=Circle(1,2,3) C.r 3
进行类型检测时,可以发现这些对象既属于创建它们时候的类,又属于tuple(这是自然,因为它们都继承自tuple)。
deque:
List比较类似数据结构中的数组,访问很方便但插入删除不便。
deque为是为了实现高效增删的双向List,可以用来构建栈和队列(当然,也可用queue模块中的Queue类,也是Python内置的队列类)。
deque在List的基础上实现了apppendleft和popleft,即可以从头进行增删。普通的List的append和pop是在尾部的增删。
d=deque(['1','2','3'])
ChainMap
将多个字典链接到一块,当多个字典中有相同的Key时,根据链接时候的先后顺序确定各字典优先级,访问Key时按优先级从高到低访问大字典中的小字典。
比如:
#command_line_args 、 os.environ 、 defaults为三个字典,其中可能存放了相同的Key combined = ChainMap( command_line_args , os.environ , defaults) #优先级为command_line_args os.environ defaults
访问 combined[Key]时,一定是按command_line_args[Key]、os.environ[Key]、defaults[Key]的先后顺序返回Value。
如果前一个字典中没有该Key,就在后一个字典中找,直到找到并返回。如果三个都有,则只返回最前边的。
Counter
Counter的本质是字典,记录各个字符出现次数的字典。
from collections import Counter c=Counter('aababcc') c Counter({'a': 3, 'b': 2, 'c': 2})
c[Key]返回,Key值出现的次数。
可以通过Counter的update函数,向Counter中加入新的需要统计的字符。
c.update('Thank you very much!') c Counter({' ': 4, 'o': 3, 'y': 3, 'h': 3, 'e': 2, 'l': 2, 'n': 2, 'u': 2, 'H': 1, 'P': 1, 't': 1, 'T': 1, 'a': 1, 'k': 1, 'v': 1, 'r': 1, 'm': 1, 'c': 1, '!': 1})