Python中 collections模块的详细用法介绍

Posted Python学习

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python中 collections模块的详细用法介绍相关的知识,希望对你有一定的参考价值。

1. 介绍

collections是Python内建的一个集合模块,提供了许多有用的集合类和方法。

可以把它理解为一个容器,里面提供Python标准内建容器 dict , list , set , 和 tuple 的替代选择。

import collections

print(dir(collections))
# [\'ChainMap\', \'Counter\', \'Mapping\', \'MutableMapping\', \'OrderedDict\', \'UserDict\', \'UserList\', \'UserString\', \'_Link\', \'_OrderedDictItemsView\', \'_OrderedDictKeysView\', \'_OrderedDictValuesView\', \'__all__\', \'__builtins__\', \'__cached__\', \'__doc__\', \'__file__\', \'__getattr__\', \'__loader__\', \'__name__\', \'__package__\', \'__path__\', \'__spec__\', \'_chain\', \'_collections_abc\', \'_count_elements\', \'_eq\', \'_heapq\', \'_iskeyword\', \'_itemgetter\', \'_nt_itemgetters\', \'_proxy\', \'_recursive_repr\', \'_repeat\', \'_starmap\', \'_sys\', \'abc\', \'defaultdict\', \'deque\', \'namedtuple\']

里面有许多方法,我们只介绍常用的方法。

2.常用方法

  • namedtuple() : 创建一个命名元组子类的工厂函数
  • deque :    高效增删改双向列表,类似列表(list)的容器,实现了在两端快速添加(append)和弹出(pop)
  • defaultdict : 当字典查询时,为key不存在提供一个默认值。
  • OrderedDict : 有序词典,就是记住了插入顺序
  • Counter : 计数功能

1. namedtuple() 命名元组

参数

collections.namedtuple(typename, field_names, *, rename=False, defaults=None, module=None)
  • typename :  命名的名字,返回一个新的元组子类,名为 typename
  • field_names : 可以是一个[\'x\', \'y\']这样的序列,也可以是\'x, y\'或者\'x y\'
  • rename :   python3.1添加,如果 rename 为真, 无效域名会自动转换成位置名。比如 [\'abc\', \'def\', \'ghi\', \'abc\'] 转换成 [\'abc\', \'_1\', \'ghi\', \'_3\'] , 消除关键词 def 和重复域名 abc 。
  • defaults :  python3.7添加, defaults 可以为 None 或者是一个默认值的 iterable(可迭代对象)。如果一个默认值域必须跟其他没有默认值的域在一起出现, defaults 就应用到最右边的参数。比如如果域名 [\'x\', \'y\', \'z\'] 和默认值 (1, 2) ,那么 x 就必须指定一个参数值 ,y 默认值 1 , z 默认值 2 。
  • module :   python3.6添加,如果 module 值有定义,命名元组的 __module__ 属性值就被设置。

使用

例如我想定义一个点(x, y),可以给它起个名字为Points

\'\'\'
学习中遇到问题没人解答?小编创建了一个Python学习交流群:531509025
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
\'\'\'
import collections

point = collections.namedtuple(\'Points\', [\'x\', \'y\'])
p1 = point(2, 3)
p2 = point(4, 2)

print(p1) # Points(x=2, y=3)
print(p2) # Points(x=4, y=2)

用 isinstance 判断其类型

print(isinstance(p1, point)) # True
print(isinstance(p1, tuple)) # True

可以发现它即属于 point 类型,也属于 tuple 类型。

使用 _make 赋值

a= [11, 3]
p1._make(a)
print(p1) # Points(x=11, y=3)

使用 _replace 更改值

p1._replace(x=5)
print(p1) # Points(x=5, y=3)

2. deque 双端队列

参数

collections.deque([iterable[, maxlen]])

返回一个新的双向队列对象,从左到右初始化(用方法 append()) ,从 iterable (迭代对象) 数据创建。如果 iterable 没有指定,新队列为空。

  • iterable :迭代对象,可以是字符串,列表等可迭代对象。
  • maxlen : maxlen 没有指定或者是 None , deque 可以增长到任意长度。否则, deque 就限定到指定最大长度。一旦限定长度的 deque 满了,当新项加入时,同样数量的项就从另一端弹出。

使用

\'\'\'
学习中遇到问题没人解答?小编创建了一个Python学习交流群:531509025
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
\'\'\'
from collections import deque

q = deque([\'a\', \'b\', \'c\'], maxlen=10)
# 从右边添加一个元素
q.append(\'d\')
print(q) # deque([\'a\', \'b\', \'c\', \'d\'], maxlen=10)

# 从左边删除一个元素
print(q.popleft()) # a
print(q) # deque([\'b\', \'c\', \'d\'], maxlen=10)

# 扩展队列
q.extend([\'i\', \'j\'])
print(q) # deque([\'b\', \'c\', \'d\', \'i\', \'j\'], maxlen=10)

# 查找下标
print(q.index(\'c\')) # 1

# 移除第一个\'d\'
q.remove(\'d\')
print(q) # deque([\'b\', \'c\', \'i\', \'j\'], maxlen=10)

# 逆序
q.reverse()
print(q) # deque([\'j\', \'i\', \'c\', \'b\'], maxlen=10)

# 最大长度
print(q.maxlen) # 10

全部方法

append(x):添加 x 到右端。

appendleft(x):添加 x 到左端。

clear():移除所有元素,使其长度为0.

copy():创建一份浅拷贝。3.5 新版功能.

count(x):计算deque中个数等于 x 的元素。3.2 新版功能.

extend(iterable):扩展deque的右侧,通过添加iterable参数中的元素。

extendleft(iterable):扩展deque的左侧,通过添加iterable参数中的元素。注意,左添加时,在结果中iterable参数中的顺序将被反过来添加。

index(x[, start[, stop]]):返回第 x 个元素(从 start 开始计算,在 stop 之前)。返回第一个匹配,如果没找到的话,升起 ValueError 。3.5 新版功能.

insert(i, x):在位置 i 插入 x 。如果插入会导致一个限长deque超出长度 maxlen 的话,就升起一个 IndexError 。3.5 新版功能.

pop():移去并且返回一个元素,deque最右侧的那一个。如果没有元素的话,就升起 IndexError 索引错误。

popleft():移去并且返回一个元素,deque最左侧的那一个。如果没有元素的话,就升起 IndexError 索引错误。

remove(value):移去找到的第一个 value。 如果没有的话就升起 ValueError 。

reverse():将deque逆序排列。返回 None 。3.2 新版功能.

rotate(n=1):向右循环移动 n 步。 如果 n 是负数,就向左循环。如果deque不是空的,向右循环移动一步就等价于 d.appendleft(d.pop()) , 向左循环一步就等价于 d.append(d.popleft()) 。

Deque对象同样提供了一个只读属性:
maxlen:Deque的最大尺寸,如果没有限定的话就是 None 。

3. defaultdict 默认值字典

使用

当key不存在时返回默认值

from collections import defaultdict
dd = defaultdict(lambda: \'not exist\')
dd[\'key1\'] = \'abc\'
print(dd[\'key1\']) # key1存在  
# \'abc\'
print(dd[\'key2\']) # key2不存在,返回默认值
# \'not exist\'

使用 list 作为 default_factory ,很容易将序列作为键值对加入字典:

from collections import defaultdict

d = defaultdict(list)
s = [(\'yellow\', 1), (\'blue\', 2), (\'yellow\', 3), (\'blue\', 4), (\'red\', 1)]

for k, v in s:
    d[k].append(v)

print(d) # defaultdict(<class \'list\'>, {\'yellow\': [1, 3], \'blue\': [2, 4], \'red\': [1]})

相当于

d = {}
s = [(\'yellow\', 1), (\'blue\', 2), (\'yellow\', 3), (\'blue\', 4), (\'red\', 1)]

for k, v in s:
    d.setdefault(k, []).append(v)

print(d) # {\'yellow\': [1, 3], \'blue\': [2, 4], \'red\': [1]}

设置 default_factory 为 int ,可以很好的用于计数

s = \'mississippi\'
d = defaultdict(int)
for k in s:
    d[k] += 1

print(d) # defaultdict(<class \'int\'>, {\'m\': 1, \'i\': 4, \'s\': 4, \'p\': 2})

4. OrderedDict 有序字典

有序词典就像常规词典一样,但有一些与排序操作相关的额外功能。

但是内置的 dict 类已经有了记住插入顺序的能力(在 Python 3.7 中保证了这种新行为),所以它变得不那么重要了。

使用

popitem(last=True) :有序字典的 popitem() 方法移除并返回一个 (key, value) 键值对。 如果 last 值为真,则按 LIFO 后进先出的顺序返回键值对,否则就按 FIFO 先进先出的顺序返回键值对。

from collections import OrderedDict

d = OrderedDict(a=1, b=2, c=3, d=4,e=5)
print(d) # OrderedDict([(\'a\', 1), (\'b\', 2), (\'c\', 3), (\'d\', 4), (\'e\', 5)])
print(d.popitem(last=True)) # (\'e\', 5)
print(d.popitem(last=False)) # (\'a\', 1)
print(d) # OrderedDict([(\'b\', 2), (\'c\', 3), (\'d\', 4)]

move_to_end(key, last=True) :将现有 key 移动到有序字典的任一端。 如果 last 为真值(默认)则将元素移至末尾;如果 last 为假值则将元素移至开头。如果 key 不存在则会触发 KeyError。

from collections import OrderedDict


d = OrderedDict(a=1, b=2, c=3, d=4,e=5)
print(d) # OrderedDict([(\'a\', 1), (\'b\', 2), (\'c\', 3), (\'d\', 4), (\'e\', 5)])

d.move_to_end(key=\'c\', last=True)
print(d) # OrderedDict([(\'a\', 1), (\'b\', 2), (\'d\', 4), (\'e\', 5), (\'c\', 3)])

d.move_to_end(key=\'b\', last=False)
print(d) # OrderedDict([(\'b\', 2), (\'a\', 1), (\'d\', 4), (\'e\', 5), (\'c\', 3)])

5. Counter 计数

Counter 是一个 dict 的子类,用于计数可哈希对象。特别方便!

使用

字符串

from collections import Counter

c = Counter()
for i in \'sfsadfsdjklgsdla\':
    c[i] +=  1

print(isinstance(c,Counter)) # True
print(isinstance(c,dict)) # True
print(c) # Counter({\'s\': 4, \'d\': 3, \'f\': 2, \'a\': 2, \'l\': 2, \'j\': 1, \'k\': 1, \'g\': 1})

c2 = Counter(\'asfjslfjsdlfjgkls\')
print(c2) # Counter({\'s\': 4, \'d\': 3, \'f\': 2, \'a\': 2, \'l\': 2, \'j\': 1, \'k\': 1, \'g\': 1})

列表

\'\'\'
学习中遇到问题没人解答?小编创建了一个Python学习交流群:531509025
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
\'\'\'
from collections import Counter

c = Counter([\'red\', \'blue\', \'red\', \'green\', \'blue\', \'blue\'])
print(c) # Counter({\'blue\': 3, \'red\': 2, \'green\': 1})

elements() :返回一个迭代器,其中每个元素将重复出现计数值所指定次。 元素会按首次出现的顺序返回。 如果一个元素的计数值小于一, elements() 将会忽略它。

c = Counter(a=4, b=2, c=0, d=-2)
print(sorted(c.elements())) # [\'a\', \'a\', \'a\', \'a\', \'b\', \'b\']

most_common([n]) :返回一个列表,其中包含 n 个最常见的元素及出现次数,按常见程度由高到低排序。 如果 n 被省略或为 None, most_common() 将返回计数器中的 所有 元素。 计数值相等的元素按首次出现的顺序排序:

c = Counter(\'abracadabra\')
print(c.most_common(3)) # [(\'a\', 5), (\'b\', 2), (\'r\', 2)]

subtract([iterable-or-mapping]) :从 迭代对象 或 映射对象 减去元素。像 dict.update() 但是是减去,而不是替换。输入和输出都可以是0或者负数。

c = Counter(a=4, b=2, c=0, d=-2)
d = Counter(a=1, b=2, c=3, d=4)
c.subtract(d)
print(c) # Counter({\'a\': 3, \'b\': 0, \'c\': -3, \'d\': -6})

python collections模块-标准库

参考老顽童博客,他写的很详细,例子也很容易操作和理解.

1.模块简介

collections包含了一些特殊的容器,针对Python内置的容器,例如list、dict、set和tuple,提供了另一种选择;

namedtuple,可以创建包含名称的tuple;

deque,类似于list的容器,可以快速的在队列头部和尾部添加、删除元素;

Counter,dict的子类,计算可hash的对象;

OrderedDict,dict的子类,可以记住元素的添加顺序;

defaultdict,dict的子类,可以调用提供默认值的函数;

2.模块使用

2.1 Counter

counter可以支持方便、快速的计数,例如,

from collections import *

cnt = Counter()
wordList = ["a","b","c","c","a","a"]
for word in wordList:
    cnt[word] += 1
print cnt

控制台输出,

Counter({a: 3, c: 2, b: 1})

对可迭代的对象进行计数或者从另一个映射(counter)进行初始化,

>>> c = Counter()#一个新的,空的counter
>>> c
Counter()
>>> c = Counter("gallahad")#从可迭代的字符串初始化counter
>>> c
Counter({a: 3, l: 2, h: 1, g: 1, d: 1})
>>> c = Counter({red:4,blue:2})#从映射初始化counter
>>> c
Counter({red: 4, blue: 2})
>>> c = Counter(cats = 4,dogs = 8)#从args初始化counter
>>> c
Counter({dogs: 8, cats: 4})

Counter对象类似于字典,如果某个项缺失,会返回0,而不是报出KeyError;

>>> c = Counter([eggs,ham])
>>> c[bacon]#没有bacon
0
>>> c[eggs]#有eggs
1

将一个元素的数目设置为0,并不能将它从counter中删除,使用del可以将这个元素删除;

>>> c
Counter({eggs: 1, ham: 1})
>>> c[eggs] = 0
>>> c
Counter({ham: 1, eggs: 0})#eggs依然存在
>>> del c[eggs]
>>> c
Counter({ham: 1})#eggs不存在

Counter对象支持以下三个字典不支持的方法,elements(),most_common(),subtract();

element(),返回一个迭代器,每个元素重复的次数为它的数目,顺序是任意的顺序,如果一个元素的数目少于1,那么elements()就会忽略它;

>>> c = Counter(a=2,b=4,c=0,d=-2,e = 1)
>>> c
Counter({b: 4, a: 2, e: 1, c: 0, d: -2})
>>> list(c.elements())
[a, a, b, b, b, b, e]

most_common(),返回一个列表,包含counter中n个最大数目的元素
,如果忽略n或者为None,most_common()将会返回counter中的所有元素,元素有着相同数目的将会以任意顺序排列;

>>> Counter(abracadabra).most_common(3)
[(a, 5), (r, 2), (b, 2)]
>>> Counter(abracadabra).most_common()
[(a, 5), (r, 2), (b, 2), (c, 1), (d, 1)]
>>> Counter(abracadabra).most_common(None)
[(a, 5), (r, 2), (b, 2), (c, 1), (d, 1)]

subtract(),从一个可迭代对象中或者另一个映射(或counter)中,元素相减,类似于dict.update(),但是subtracts 数目而不是替换它们,输入和输出都有可能为0或者为负;

>>> c = Counter(a=4,b=2,c=0,d=-2)
>>> d = Counter(a=1,b=2,c=-3,d=4)
>>> c.subtract(d)
>>> c
Counter({a: 3, c: 3, b: 0, d: -6})

update(),从一个可迭代对象中或者另一个映射(或counter)中所有元素相加,类似于dict.upodate,是数目相加而非替换它们,另外,可迭代对象是一个元素序列,而非(key,value)对构成的序列;

>>> c
Counter({a: 4, b: 2, c: 0, d: -2})
>>> d
Counter({d: 4, b: 2, a: 1, c: -3})
>>> c.update(d)
>>> c
Counter({a: 5, b: 4, d: 2, c: -3})

Counter对象常见的操作,

>>> c
Counter({a: 5, b: 4, d: 2, c: -3})
>>> sum(c.values())# 统计所有的数目
8
>>> list(c)# 列出所有唯一的元素
[a, c, b, d]
>>> set(c)# 转换为set
set([a, c, b, d])
>>> dict(c)# 转换为常规的dict
{a: 5, c: -3, b: 4, d: 2}
>>> c.items()# 转换为(elem,cnt)对构成的列表
[(a, 5), (c, -3), (b, 4), (d, 2)]
>>> c.most_common()[:-4:-1]# 输出n个数目最小元素
[(c, -3), (d, 2), (b, 4)]
>>> c += Counter()# 删除数目为0和为负的元素
>>> c
Counter({a: 5, b: 4, d: 2})
>>> Counter(dict(c.items()))# 从(elem,cnt)对构成的列表转换为counter
Counter({a: 5, b: 4, d: 2})
>>> c.clear()# 清空counter
>>> c
Counter()

在Counter对象进行数学操作,得多集合(counter中元素数目大于0)加法和减法操作,是相加或者相减对应元素的数目;交集和并集返回对应数目的最小值和最大值;每个操作均接受暑促是有符号的数目,但是输出并不包含数目为0或者为负的元素;

>>> c = Counter(a=3,b=1,c=-2)
>>> d = Counter(a=1,b=2,c=4)
>>> c+d#求和
Counter({a: 4, b: 3, c: 2})
>>> c-d#求差
Counter({a: 2})
>>> c & d#求交集
Counter({a: 1, b: 1})
>>> c | d#求并集
Counter({c: 4, a: 3, b: 2})

2.2 deque

deque是栈和队列的一种广义实现,deque是"double-end queue"的简称;deque支持线程安全、有效内存地以近似O(1)的性能在deque的两端插入和删除元素,尽管list也支持相似的操作,但是它主要在固定长度操作上的优化,从而在pop(0)和insert(0,v)(会改变数据的位置和大小)上有O(n)的时间复杂度。

deque支持如下方法,

append(x), 将x添加到deque的右侧;

appendleft(x), 将x添加到deque的左侧;

clear(), 将deque中的元素全部删除,最后长度为0;

count(x), 返回deque中元素等于x的个数;

extend(iterable), 将可迭代变量iterable中的元素添加至deque的右侧;

extendleft(iterable), 将变量iterable中的元素添加至deque的左侧,往左侧添加序列的顺序与可迭代变量iterable中的元素相反;

pop(), 移除和返回deque中最右侧的元素,如果没有元素,将会报出IndexError;

popleft(), 移除和返回deque中最左侧的元素,如果没有元素,将会报出IndexError;

remove(value), 移除第一次出现的value,如果没有找到,报出ValueError;

reverse(), 反转deque中的元素,并返回None;

rotate(n), 从右侧反转n步,如果n为负数,则从左侧反转,d.rotate(1)等于d.appendleft(d.pop());

maxlen, 只读的属性,deque的最大长度,如果无解,就返回None;

除了以上的方法之外,deque还支持迭代、序列化、len(d)、reversed(d)、copy.copy(d)、copy.deepcopy(d),通过in操作符进行成员测试和下标索引,索引的时间复杂度是在两端是O(1),在中间是O(n),为了快速获取,可以使用list代替。

>>> from collections import deque
>>> d = deque(ghi)# 新建一个deque,有三个元素
>>> for ele in d:# 遍历deque
...     print ele.upper()
...     
... 
G
H
I
>>> d.append(j)# deque右侧添加一个元素
>>> d.appendleft(f)# deque左侧添加一个元素
>>> d# 打印deque
deque([f, g, h, i, j])
>>> d.pop()# 返回和移除最右侧元素
j
>>> d.popleft()# 返回和移除最左侧元素
f
>>> list(d)# 以列表形式展示出deque的内容
[g, h, i]
>>> d[0]# 获取最左侧的元素
g
>>> d[-1]# 获取最右侧的元素
i
>>> list(reversed(d))# 以列表形式展示出倒序的deque的内容
[i, h, g]
>>> h in d# 在deque中搜索
True
>>> d.extend(jkl)# 一次添加多个元素
>>> d
deque([g, h, i, j, k, l])
>>> d.rotate(1)# 往右侧翻转
>>> d
deque([l, g, h, i, j, k])
>>> d.rotate(-1)# 往左侧翻转
>>> d
deque([g, h, i, j, k, l])
>>> deque(reversed(d))# 以逆序新建一个deque
deque([l, k, j, i, h, g])
>>> d.clear()# 清空deque
>>> d.pop()# 不能在空的deque上pop
Traceback (most recent call last):
  File "<input>", line 1, in <module>
IndexError: pop from an empty deque
>>> d.extendleft(abc)# 以输入的逆序向左扩展
>>> d
deque([c, b, a])

其他的应用:

1.限定长度的deque提供了Unix中tail命令相似的功能;

from collections import deque

def tail(filename,n = 10):
    "Return the last n lines of a file"
    return deque(open(filename),n)

print tail("temp.txt",10)

2.使用deque维护一个序列(右侧添加元素,左侧删除元素)中窗口的平均值;

from collections import deque
import itertools

def moving_average(iterable,n = 3):
    it = iter(iterable)
    d = deque(itertools.islice(it,n-1))
    # 第一次只有两个元素,再右移的过程中,需要先删除最左端的元素,因此现在最左端加入0
    d.appendleft(0)
    s = sum(d)
    for ele in it:
        # 删除最左端的元素,再加上新元素
        s += ele - d.popleft()
        # 右端添加新元素
        d.append(ele)
        yield s / float(n)

array = [40,30,50,46,39,44]
for ele in moving_average(array,n=3):
    print ele

3.rotate()方法提供了一种实现deque切片和删除的方式,例如,del d[n]依赖于rotate方法的纯Python实现,如下,

from collections import deque

def delete_nth(d,n):
    # 将前n个元素翻转到右侧
    d.rotate(-n)
    # 删除第n个元素
    d.popleft()
    # 再将后n个元素翻转到左侧
    d.rotate(n)

d = deque("abcdefg")
delete_nth(d,n = 3)
print d

4.slice依赖于rotate方法的纯Python实现,如下,

from collections import deque

def slice(d,m,n):
    # 先将前面m个元素翻转到右侧
    d.rotate(-m)
    i = m
    sliceList = []
    # 依次将[m,n]区间内的元素出栈
    while i < n:
        item = d.popleft()
        sliceList.append(item)
        i+=1
    # 再将出栈的元素扩展到deque右侧
    d.extend(sliceList)
    # 再将后面n个元素翻转到左侧
    d.rotate(n)
    return sliceList

d = deque("abcdefg")
print slice(d,1,5)

2.3 defaultdict

defaultdict是内置数据类型dict的一个子类,基本功能与dict一样,只是重写了一个方法__missing__(key)和增加了一个可写的对象变量default_factory。

>>> dir(defaultdict)
[__class__, __cmp__, __contains__, __copy__, __delattr__, __delitem__, __doc__, __eq__, __format__, __ge__, __getattribute__, __getitem__
, __gt__, __hash__, __init__, __iter__, __le__, __len__, __lt__,__missing__, __ne__, __new__, __reduce__, __reduce_ex__, __repr__, __setattr__, __setitem__, __sizeof__, __str__, __subclasshook__, clear
, copy, default_factory, fromkeys, get, has_key, items, iteritems, iterkeys, itervalues, keys, pop, popitem, setdefault, update, v
alues, viewitems, viewkeys, viewvalues]
>>> dir(dict)
[__class__, __cmp__, __contains__, __delattr__, __delitem__, __doc__
, __eq__, __format__, __ge__, __getattribute__, __getitem__, __gt__,
 __hash__, __init__, __iter__, __le__, __len__, __lt__, __ne__, _
_new__, __reduce__, __reduce_ex__, __repr__, __setattr__, __setitem__, __sizeof__, __str__, __subclasshook__, clear, copy, fromkeys, get, has_key, items, iteritems, iterkeys, itervalues, keys, pop, po
pitem, setdefault, update, values, viewitems, viewkeys, viewvalues]

missing(key)

  1. 如果default_factory属性为None,就报出以key作为遍历的KeyError异常;
  2. 如果default_factory不为None,就会向给定的key提供一个默认值,这个值插入到词典中,并返回;
  3. 如果调用default_factory报出异常,这个异常在传播时不会改变;
  4. 这个方法是当要求的key不存在时,dict类中的__getitem()__方法所调用,无论它返回或者报出什么,最终返回或报出给__getitem()__;
  5. 只有__getitem__()才能调用__missing__(),这意味着,如果get()起作用,如普通的词典,将会返回None作为默认值,而不是使用default_factory;

default_factory, 这个属性用于__missing__()方法,使用构造器中的第一个参数初始化;

使用list作为default_factory,很容易将一个key-value的序列转换为一个关于list的词典;

>>> from collections import *
>>> s = [(yellow,1),(blue,2),(yellow,3),(blue,4),(red,5)]
>>> d = defaultdict(list)
>>> for k,v in s: d[k].append(v)
... 
>>> d.items()
[(blue, [2, 4]), (red, [5]), (yellow, [1, 3])]

当每一个key第一次遇到时,还没有准备映射,首先会使用default_factory函数自动创建一个空的list,list.append()操作将value添加至新的list中,当key再次遇到时,通过查表,返回对应这个key的list,list.append()会将新的value添加至list,这个技术要比dict.setdefault()要简单和快速。

>>> e = {}
>>> for k,v in s: e.setdefault(k,[]).append(v)
... 
>>> e.items()
[(blue, [2, 4]), (red, [5]), (yellow, [1, 3])]

设置default_factory为int,使得defaultdict可以用于计数,

>>> s = "mississippi"
>>> d = defaultdict(int)
>>> for k in s: d[k]+=1
... 
>>> d.items()
[(i, 4), (p, 2), (s, 4), (m, 1)]

当一个字母第一次遇到,默认从default_factory中调用int()用于提供一个默认为0的计数,递增操作会增加每个字母的计数。

函数int()经常返回0,是常量函数的一种特例。一种更快和更灵活的创建常量函数的方式是使用itertools.repeat(),可以提供任意常量值(不仅仅是0),

>>> import itertools
>>> def constant_factory(value):
...     return itertools.repeat(value).next
... 
>>> d = defaultdict(constant_factory(<missing>))
>>> d.update(name = "John",action = "ran")
>>> "%(name)s %(action)s to %(object)s"%d
John ran to <missing>

将default_factory设置为set,使得defaultdict可以建立一个关于set的词典,

>>> s = [(red, 1), (blue, 2), (red, 3), (blue, 4), (red, 1), (blue,
4)]
>>> d = defaultdict(set)
>>> for k,v in s:d[k].add(v)
... 
>>> d.items()
[(blue, set([2, 4])), (red, set([1, 3]))]

2.4 namedtuple

命名的元组,意味给元组中的每个位置赋予含义,意味着代码可读性更强,namedtuple可以在任何常规元素使用的地方使用,而且它可以通过名称来获取字段信息而不仅仅是通过位置索引。

>>> from collections import *
>>> Point = namedtuple(Point,[x,y],verbose = True)
class Point(tuple):
    Point(x, y)

    __slots__ = ()

    _fields = (x, y)

    def __new__(_cls, x, y):
        Create new instance of Point(x, y)
        return _tuple.__new__(_cls, (x, y))

    @classmethod
    def _make(cls, iterable, new=tuple.__new__, len=len):
        Make a new Point object from a sequence or iterable
        result = new(cls, iterable)
        if len(result) != 2:
            raise TypeError(Expected 2 arguments, got %d % len(result))
        return result

    def __repr__(self):
        Return a nicely formatted representation string
        return Point(x=%r, y=%r) % self

    def _asdict(self):
        Return a new OrderedDict which maps field names to their values
        return OrderedDict(zip(self._fields, self))

    def _replace(_self, **kwds):
        Return a new Point object replacing specified fields with new values
        result = _self._make(map(kwds.pop, (x, y), _self))
        if kwds:
            raise ValueError(Got unexpected field names: %r % kwds.keys())
        return result

    def __getnewargs__(self):
        Return self as a plain tuple.  Used by copy and pickle.
        return tuple(self)

    __dict__ = _property(_asdict)

    def __getstate__(self):
        Exclude the OrderedDict from pickling
        pass

    x = _property(_itemgetter(0), doc=Alias for field number 0)

    y = _property(_itemgetter(1), doc=Alias for field number 1)
>>> p = Point(11,y = 22)# 实例化一个对象,可以使用位置或者关键字
>>> p[0] + p[1]# 通过索引访问元组中的元素
33
>>> x,y = p# 分开,类似于常规的元组
>>> x,y
(11, 22)
>>> p.x + p.y# 通过名称访问元素
33
>>> p# 可读的__repr__,通过name = value风格
Point(x=11, y=22)
namedtuple在给csv或者sqlite3返回的元组附上名称特别有用,
from collections import *
import csv

EmployeeRecord = namedtuple(EmployeeRecord,name, age, title, department, paygrade)
for emp in map(EmployeeRecord._make,csv.reader(open("employee.csv","rb"))):
    print emp.name,emp.title

# import sqlite3
# conn = sqlite3.connect(‘/companydata‘)
# cursor = conn.cursor()
# cursor.execute(‘SELECT name, age, title, department, paygrade FROM employees‘)
# for emp in map(EmployeeRecord._make, cursor.fetchall()):
#     print emp.name, emp.title

控制台输出,

Jim RD
Tom Manager

除了从tuples继承的方法之外,namedtuple还支持三种方法和一个属性,为了避免和名称冲突,这些方法和属性以下划线开始。

**somenamedtuple._make(),** 从已有的序列或者可迭代的对象中创建一个新的对象;

>>> Point = namedtuple(Point, [x, y])
>>> t = [33,44]
>>> Point._make(t)
Point(x=33, y=44)

**somenamedtuple._asdict(),** 返回一个OrderDict,由名称到对应值建立的映射;

>>> p = Point(x = 11,y = 22)
>>> p
Point(x=11, y=22)
>>> pDict = p._asdict()
>>> pDict
OrderedDict([(x, 11), (y, 22)])

**somenamedtuple._replace(),** 返回一个新的namedtuple对象,用新值替换指定名称中的值;

>>> p2 = p._replace(x = 33)
>>> p2
Point(x=33, y=22)

**somenamedtuple._fields,** 以字符串构成的元组的形式返回namedtuple中的名称,在自省或者基于一个已经存在的namedtuple中创建新的namedtuple时,非常有用;

>>> p._fields
(x, y)
>>> Color = namedtuple(Color,"red green blu")
>>> Pixel = namedtuple(Pixel,Point._fields + Color._fields)
>>> Pixel(11,22,128,255,0)
Pixel(x=11, y=22, red=128, green=255, blu=0)

当名称存储在字符串中,可以使用getattr()函数进行检索,

>>> getattr(p,x)
11

使用**操作符,可以将一个字典转换成namedtuple,

>>> d  = {x:11,y:22}
>>> Point(**d)
Point(x=11, y=22)

由于namedtuple也是Python中的一个类,因此再子类中,它很容易添加或者修改一些功能,如下是添加一个可计算名称和固定长度的输出格式;子类中的__slots__是一个空的元组,可以通过避免词典实例的创建来节约内存开销;

class Point(namedtuple(Point,x y)):
    __slots__ = ()
    @property
    def hypot(self):
        return (self.x ** 2 + self.y**2) ** 0.5
    def __str__(self):
        return "Point:x = %6.3f  y = %6.3f  hypot = %6.3f" %(self.x,self.y,self.hypot)

for p in Point(3,4),Point(14,5/7.):
    print p

控制台输出,

Point:x =  3.000  y =  4.000  hypot =  5.000
Point:x = 14.000  y =  0.714  hypot = 14.018

子类在增加、存储名称时,并不是非常有用,相反,可以容易地通过_fields属性来创建一个新的namedtuple;

>>> Point3D = namedtuple("Point3D",Point._fields + (z,))
>>> Point3D._fields
(x, y, z)

默认值可以通过_replace()来实现,以便于标准化一个原型实例;

>>> Account = namedtuple(Account,owner balance transaction_count)
>>> default_account = Account(<owner name>,0.0,0)
>>> johns_account = default_account._replace(owner = "John")
>>> johns_account
Account(owner=John, balance=0.0, transaction_count=0)

枚举类型常量可以通过namedtuple来实现,更简单和有效的方式是通过意见简单的类声明;

Status = namedtuple(Status,open pending closed)._make(range(3))
print Status

class Status:
open, pending, closed = range(3)

print Status.open
print Status.pending
print Status.closed

控制台输出,

Status(open=0, pending=1, closed=2)
0
1
2

2.5 OrderedDict

OrderedDict类似于正常的词典,只是它记住了元素插入的顺序,当在有序的词典上迭代时,返回的元素就是它们第一次添加的顺序。

class collections.OrderedDict,返回已给dict的子类,支持常规的dict的方法,OrderedDict是一个记住元素首次插入顺序的词典,如果一个元素重写已经存在的元素,那么原始的插入位置保持不变,如果删除一个元素再重新插入,那么它就在末尾。

OrderedDict.popitem(last=True),popitem方法返回和删除一个(key,value)对,如果last=True,就以LIFO方式执行,否则以FIFO方式执行。

OrderedDict也支持反向迭代,例如reversed()。

OrderedDict对象之间的相等测试,例如,list(od1.items()) == list(od2.items()),是对顺序敏感的;OrderedDict和其他的映射对象(例如常规的词典)之间的相等测试是顺序不敏感的,这就允许OrderedDict对象可以在使用常规词典的地方替换掉常规词典。

OrderedDict构造器和update()方法可以接受关键字变量,但是它们丢失了顺序,因为Python的函数调用机制是将一个无序的词典传入关键字变量。

一个有序的词典记住它的成员插入的顺序,可以使用排序函数,将其变为排序的词典,

>>> d = {"banana":3,"apple":2,"pear":1,"orange":4}
>>> # dict sorted by key
>>> OrderedDict(sorted(d.items(),key = lambda t:t[0]))
OrderedDict([(apple, 2), (banana, 3), (orange, 4), (pear, 1)])
>>> # dict sorted by value
>>> OrderedDict(sorted(d.items(),key = lambda t:t[1]))
OrderedDict([(pear, 1), (apple, 2), (banana, 3), (orange, 4)])
>>> # dict sorted by length of key string
>>>a =  OrderedDict(sorted(d.items(),key = lambda t:len(t[0])))
>>>a
OrderedDict([(pear, 1), (apple, 2), (orange, 4), (banana, 3)])
>>> del a[apple]
>>> a
OrderedDict([(pear, 1), (orange, 4), (banana, 3)])
>>> a["apple"] = 2
>>> a
OrderedDict([(pear, 1), (orange, 4), (banana, 3), (apple, 2)])

当元素删除时,排好序的词典保持着排序的顺序;但是当新元素添加时,就会被添加到末尾,就不能保持已排序。

创建一个有序的词典,可以记住最后插入的key的顺序,如果一个新的元素要重写已经存在的元素,那么原始的插入位置就会改变成末尾,

>>> class LastUpdatedOrderedDict(OrderedDict):
...     def __setitem__(self,key,value):
...         if key in self:
...             del self[key]
...         OrderedDict.__setitem__(self, key, value)
... 
>>> obj = LastUpdatedOrderedDict()
>>> obj["apple"] = 2
>>> obj["windows"] = 3
>>> obj
LastUpdatedOrderedDict([(apple, 2), (windows, 3)])
>>> obj["apple"] = 1
>>> obj
LastUpdatedOrderedDict([(windows, 3), (apple, 1)])

一个有序的词典可以和Counter类一起使用,counter对象就可以记住元素首次出现的顺序;

class OrderedCounter(Counter,OrderedDict):
    def __repr__(self):
        return "%s(%r)"%(self.__class__.__name__,OrderedDict(self))

    def __reduce__(self):
        return self.__class__,(OrderedDict(self))

#和OrderDict一起使用的Counter对象
obj = OrderedCounter()
wordList = ["b","a","c","a","c","a"]
for word in wordList:
    obj[word] += 1
print obj

# 普通的Counter对象
cnt = Counter()
wordList = ["b","a","c","a","c","a"]
for word in wordList:
    cnt[word] += 1
print cnt

控制台输出,

OrderedCounter(OrderedDict([(b, 1), (a, 3), (c, 2)]))
Counter({a: 3, c: 2, b: 1})

3.Reference

8.3. collections — High-performance container datatypes


以上是关于Python中 collections模块的详细用法介绍的主要内容,如果未能解决你的问题,请参考以下文章

(转)python collections模块详解

Python模块: collections

python(43):collections模块

Python collections 模块用法举例

python collections 模块

Python3之collections模块