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 |
应用需求:我们知道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)
somenamedtuple.
_make
(iterable)除了上面提到的初始化方法,还可以通过iterable来实例化对象。
>>> 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
应用需求:queue数据结构的数据是先进先出——只能从一个方向插入数据,从另一侧排出数据。如果希望两边都可以pop数据,可以使用deque数据结构。deque的全称是“double-ended queue”。
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。
应用对象:将多个dict整合到同一个列表中,存在一个key对应多个value的情况(多个dict中存在相同的key),以第一个出现的k键值对为准,后面出现的将被忽略。
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对象,有几点需要说明一下:
>>> z
ChainMap({'b': 10, 'c': 11}, {'a': 1, 'b': 2})
>>> z['b']
10
>>> z['a']
1
>>> z['e']
raise KeyError(key)
KeyError: 'e'
>>> 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.
>>> 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})
应用对象:统计list或iterable中每个元素出现的次数,返回Counter对象,其中包含元素和元素的次数为键值对的字典。
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()
>>> 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)]
应用需求:我们知道dict中的键值对是“无序”存放的,有时候我们却希望dict中键值对按照插入的先后顺序排列,此时就可以使用OrderDict类。
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)])
应用需求:当key不存在时,dict会返回一个找不到key时的方法,比如list、set、int、float等等,而不是当找不到key时抛出KeyError异常。
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(, {'red': [1, 3, 1], 'blue': [2, 4, 4]})