Collections模块中的每一个类都是为了补充python内置数据类型list和dict的功能,在某些特性情况下使用Collections中的类将取得更好的效果。
Collections模块中所包含的类:
namedtuple() | factory function for creating tuple subclasses with named fields |
deque | list-like container with fast appends and pops on either end |
ChainMap | dict-like class for creating a single view of multiple mappings |
Counter | dict subclass for counting hashable objects |
OrderedDict | dict subclass that remembers the order entries were added |
defaultdict | dict subclass that calls a factory function to supply missing values |
UserDict | |
UserList | |
UserString |
namedtuple(对tuple的扩展)
应用需求:我们知道tuple中的元素只能通过元素在tuple中的位置进行索引,最大的问题是元素的位置很难与元素的真实含义相结合,毕竟代码是要让人看得懂的。如果tuple中元素太多将对代码维护造成极大的挑战。
namedtuple就是为了解决上面提到的痛点,通过给tuple中的元素赋予一个名字——可以认为是key,通过key来访问value,这样就能解决位置索引给阅读代码者造成的烦恼。
> collections.
namedtuple
(typename, field_names, *, verbose=False, rename=False, module=None)
typename是namedtuple类的名字——一个字符串,我们习惯将namedtuple返回值的名字设成与typename相同,typename要以字符串的形式表示。执行namedtuple(typename, field_names)后会返回一个tuple子类,每个元素的别名用field_names中的字符串来命名。
field_names是tuple元素的别名——可以认为是key,这样可以避免使用位置去索引tuple元素。field_names由多个field_name构成,每个field_name必须为字符串。field_name可以通过两种方式来构成field_names,一种是将全部field_name装入list中(['a','b','c','d']),另外一种是将field_name全部装入一个字符串中,field_name之间用分隔符(空格,逗号,tab)隔开('a b c d'或者'a,b,c')。特别要注意的是,namedtuple生成的是一个类,而不是对象。
>>> Point1 = namedtuple('Point','x,y,z')
>>> Point2 = namedtuple('Point','x y z')
>>> Point3 = namedtuple('Point',['x', 'y', 'z'])
生成namedtuple类后需要实例化对象,namedtuple类在初始化的时候有几个别名,实例化的时候就需要提供几个参数。既可以直接带入参数,也可以以“别名=值”的方式实例化对象。
>>> p=Point1(1,2,3)
>>> p
Point(x=1, y=2, z=3)
>>> q=Point(z=33,y=22,x=11)
>>> q
Point(x=11, y=22, z=33)
> classmethod somenamedtuple.
_make
(iterable)
除了上面提到的初始化方法,还可以通过iterable来实例化对象。
>>> Point
<class '__main__.Point'>
>>> p=Point._make([11,12,13])
>>> p
Point(x=11, y=12, z=13)
这个函数的好处是,可以自动给namedtuple对象赋值,也就意味着可以把_make()函数作为其它函数的输入参数(工厂方法)。
EmployeeRecord = namedtuple('EmployeeRecord', 'name, age, title, department, paygrade')
import csv
for emp in map(EmployeeRecord._make, csv.reader(open("employees.csv", "rb"))):
print(emp.name, emp.title)
> somenamedtuple.
_asdict
()
返回一个OrderedDict,其中的键值对是namedtuple中的别名及其对应的值。
>>> p
Point(x=11, y=12, z=13)
>>> p._asdict()
OrderedDict([('x', 11), ('y', 12), ('z', 13)])
> somenamedtuple.
_replace
(kwargs)
实例化namedtuple对象后,如果想替换某些元素的值,可以使用_replace方法。
>>> p
Point(x=11, y=12, z=13)
>>> p._replace(x=33)
Point(x=33, y=12, z=13)
可以使用拆包的方式作为_replace()的参数。
>>> p
Point(x=11, y=12, z=13)
>>> new_point = {'x':100,'y':101,'z':102}
>>> p._replace(**new_point)
Point(x=100, y=101, z=102)
> somenamedtuple.
_fields
返回全部tuple元素的别名。
>>> p._fields # view the field names
('x', 'y')
>>> Color = namedtuple('Color', 'red green blue') # Three tuple: red, green, blue
>>> Pixel = namedtuple('Pixel', Point._fields + Color._fields) # Five tuple: x,y,red,green,blue
>>> Pixel(11, 22, 128, 255, 0)
Pixel(x=11, y=22, red=128, green=255, blue=0)
如果只想得到namedtuple对象中某个属性的值,可以使用内置函数getattr(),或者者直接使用“点”符号。
>>> p
Point(x=11, y=12, z=13)
>>> getattr(p,'z')
13
>>> getattr(p,'y')
12
>>> p.x
11
deque(对list的扩展)
应用需求:queue数据结构的数据是先进先出——只能从一个方向插入数据,从另一侧排出数据。如果希望两边都可以pop数据,可以使用deque数据结构。deque的全称是“double-ended queue”。
> class collections.
deque
([iterable[, maxlen]])
返回deque对象,其中的元素是iterable。如果iterable为空,则返回一个空的deque对象。
maxlen指定deque的最大长度,如果没有指定该参数的值,deque可以无限增长。如果iterable的长度比maxlen大,后进入的数据会把先进入的“挤出去”,直到iterable被全部遍历。
>>> deque(range(10))
deque([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> deque(range(100),10) #限定长度后,后进的会把先进的“挤出去”
deque([90, 91, 92, 93, 94, 95, 96, 97, 98, 99], maxlen=10)
正如list有append、extend、clear、remove、insert、pop、reverse方法一样,deque也有上面提到的方法,除了这些方法外,还有一些是deque数据结构特有的。
> appendleft
(x)
功能类似append(),只不过是在左侧开始位置添加参数x。
> extendleft
(iterable)
功能类似extend(),只不过是在左侧开始位置添加iterable。
> popleft
()
功能类似pop(),只不过是把左侧开始位置的数据“挤出去”,并返回该值。
> rotate
(n)
将deque中的全部元素循环右移n位。如果n是负数,则循环左移n位。
> maxlen
返回deque对象中maxlen的值,如果deque没有设定maxlen参数,返回None。
ChainMap(对dict的扩展)
应用对象:将多个dict整合到同一个列表中,存在一个key对应多个value的情况(多个dict中存在相同的key),以第一个出现的k键值对为准,后面出现的将被忽略。
> class collections.
ChainMap
(*maps)
返回一个ChainMap对象(可以认为是一个list,其中的元素全部都是maps对象(dict))。其中,如果maps为空,ChainMap中只有一个空的dict;如果map不为空,ChainMap保存的是全部maps对象。
>>> x = {'b': 10, 'c': 11}
>>> y = {'a': 1, 'b': 2}
>>> z = ChainMap(x, y)
>>> z
ChainMap({'b': 10, 'c': 11}, {'a': 1, 'b': 2})
>>> ChainMap()
ChainMap({})
关于ChainMap对象,有几点需要说明一下:
- 查询ChainMap键值对的方式与dict一样,不同的地方是ChainMap会遍历列表中的全部dict,直到遇到第一个键值对。如果查询的键值对不存在,会抛出KeyError异常
>>> z
ChainMap({'b': 10, 'c': 11}, {'a': 1, 'b': 2})
>>> z['b']
10
>>> z['a']
1
>>> z['e']
raise KeyError(key)
KeyError: 'e'
- 修改键值对的方式与dict一样,不同的是只会修改ChainMap列表中的第一个dict,从下面的列子可知,当修改key['b']=100的时候,只会修改列表中的一个dict;新增键值对key['a']=101的时候,只在第一个dict中增加了该键值对
>>> z
ChainMap({'b': 10, 'c': 11}, {'a': 1, 'b': 2})
>>> z['b']=100
>>> z['a']=101
>>> z
ChainMap({'b': 100, 'c': 11, 'a': 101}, {'a': 1, 'b': 2})
正如Python Documentation中所描述的:
Lookups search the underlying mappings successively until a key is found. In contrast, writes, updates, and deletions only operate on the first mapping.
- dict中的方法在ChainMap中可以被正常使用,需要注意的是items()方法,该方法会返回一个由key和value构成的tuple。由于ChainMap中相同key可能会存在多个value,为了避免冲突,原则还是以第一个出现的键值对为准
>>> z.items()
ItemsView(ChainMap({'b': 100, 'c': 11, 'a': 101}, {'a': 1, 'b': 2}))
>>> list(z.items())
[('b', 100), ('c', 11), ('a', 101)]
> maps
将ChainMap以list方式返回,体现出ChainMap本质上是装着多个dict的列表。
>>> z
ChainMap({'b': 100, 'c': 11, 'a': 101}, {'a': 1, 'b': 2})
>>> z.maps
[{'b': 100, 'c': 11, 'a': 101}, {'a': 1, 'b': 2}]
> new_child
(m=None)
返回一个新的ChainMap,第一个元素是m,后面紧跟着原来的dict列表。如果new_child()函数为空,则新ChainMap的第一个dict为空。
>>> z.new_child() # First dict is empty
ChainMap({}, {'b': 100, 'c': 11, 'a': 101}, {'a': 1, 'b': 2})
>>> z.new_child({'a': 1, 'b':2}) # First dict is new
ChainMap({'a': 1, 'b': 2}, {'b': 100, 'c': 11, 'a': 101}, {'a': 1, 'b': 2})
> parents
返回ChainMap中第二个及之后的dict,第一个dict将被忽略。
>>> z
ChainMap({'b': 100, 'c': 11, 'a': 101}, {'a': 1, 'b': 2})
>>> z.parents
ChainMap({'a': 1, 'b': 2})
Counter(对dict的扩展)
应用对象:统计list或iterable中每个元素出现的次数,返回Counter对象,其中包含元素和元素的次数为键值对的字典。
> class collections.
Counter
([iterable-or-mapping])
如果想统计list中元素出现的次数,一个非常简单的方式是使用Counter(),如下面的例子:
>>> Counter(['red', 'blue', 'red', 'green', 'blue', 'blue'])
Counter({'blue': 3, 'red': 2, 'green': 1})
>>> Counter('aaaffereffswfwgffdfsdf')
Counter({'f': 9, 'a': 3, 'e': 2, 's': 2, 'w': 2, 'd': 2, 'r': 1, 'g': 1})
生成Counter对象后,可以调用list、set、tuple、dict生成对应类型的数据。
>>> dict(Counter('aaaffereffswfwgffdfsdf'))
{'a': 3, 'f': 9, 'e': 2, 'r': 1, 's': 2, 'w': 2, 'g': 1, 'd': 2}
>>> list(Counter('aaaffereffswfwgffdfsdf'))
['a', 'f', 'e', 'r', 's', 'w', 'g', 'd']
>>> set(Counter('aaaffereffswfwgffdfsdf'))
{'r', 'd', 's', 'e', 'f', 'g', 'a', 'w'}
>>> dict(Counter('aaaffereffswfwgffdfsdf'))
{'a': 3, 'f': 9, 'e': 2, 'r': 1, 's': 2, 'w': 2, 'g': 1, 'd': 2}
>>> c.items()
dict_items([('a', 3), ('f', 9), ('e', 2), ('r', 1), ('s', 2), ('w', 2), ('g', 1), ('d', 2)])
> elements
()
生成Counter对象后,如果想知道全部的key,使用elements()。
>>> c
Counter({'f': 9, 'a': 3, 'e': 2, 's': 2, 'w': 2, 'd': 2, 'r': 1, 'g': 1})
>>> c.elements()
<itertools.chain object at 0x01FFC250>
>>> list(c.elements())
['a', 'a', 'a', 'f', 'f', 'f', 'f', 'f', 'f', 'f', 'f', 'f', 'e', 'e', 'r', 's', 's', 'w', 'w', 'g', 'd', 'd']
> most_common
([n])
生成Counter对象后,有种情况是键值对太多,我们只在乎出现频率最高的几项,可以使用该方法,参数n代表前n个最常出现的键值对。
>>> c
Counter({'f': 9, 'a': 3, 'e': 2, 's': 2, 'w': 2, 'd': 2, 'r': 1, 'g': 1})
>>> c.most_common(3)
[('f', 9), ('a', 3), ('e', 2)]
>>> # Find the ten most common words in Hamlet
>>> import re
>>> words = re.findall(r'\w+', open('hamlet.txt').read().lower())
>>> Counter(words).most_common(10)
[('the', 1143), ('and', 966), ('to', 762), ('of', 669), ('i', 631),
('you', 554), ('a', 546), ('my', 514), ('hamlet', 471), ('in', 451)]
OrderedDict(对dict的扩展)
应用需求:我们知道dict中的键值对是“无序”存放的,有时候我们却希望dict中键值对按照插入的先后顺序排列,此时就可以使用OrderDict类。
> class collections.
OrderedDict
([items])
输入参数是item类型,即(key, value)构成的tuple,返回值是有序字典。如果items中存在一个key对应多个value,只会保存第一个出现的键值对,后面的会被忽略。
>>> s
[('red', 1), ('blue', 2), ('red', 3), ('blue', 4), ('red', 1), ('blue', 4)]
>>> OrderedDict(s)
OrderedDict([('red', 1), ('blue', 4)])
如果不带参数,将创建一个空的OrderedDict对象,可以像字典的处理方式给OrderedDict添加对象。先被插入的键值对将放到后被插入的前面,这正是有序字典的特点。
>>> a=OrderedDict()
>>> a
OrderedDict()
>>> a['red']=1
>>> a['blue']=2
>>> a
OrderedDict([('red', 1), ('blue', 2)])
将OrderedDict对象作为list、set、dict的参数,可以转化为对应的类型。
>>> a
OrderedDict([('red', 1), ('blue', 2)])
>>> dict(a)
{'red': 1, 'blue': 2}
>>> list(a)
['red', 'blue']
>>> set(a)
{'blue', 'red'}
>>> a.items()
odict_items([('red', 1), ('blue', 2)])
defaultdict(对dict的扩展)
应用需求:当key不存在时,dict会返回一个找不到key时的方法,比如list、set、int、float等等,而不是当找不到key时抛出KeyError异常。
> class collections.
defaultdict
([default_factory[, ...]])
default_factory表示工厂方法,使用defaultdict将创建(返回)一个字典,字典的值都是default_factory类型。一般我们会把default_factory设成list或set,将数据保存到list或set中。下面的例子是将default_factory设成list,然后把相同key的value保存一起。
>>> s = [('red', 1), ('blue', 2), ('red', 3), ('blue', 4), ('red', 1), ('blue', 4)]
>>> d = defaultdict(list)
>>> for k,v in s:
d[k].append(v)
>>> d
defaultdict(<class 'list'>, {'red': [1, 3, 1], 'blue': [2, 4, 4]})