Python collections 模块笔记

namedtuple

collections.namedtuple 是一个工厂函数,它能够用来构建一个带字段名的元组和一个有名字的类——这个带名字的类对调试程序有很大帮助。php

咱们能够这样建立一个 User 类:html

Card = collections.namedtuple('User', ['name', 'age', 'height'])
复制代码

如何用具名元组来记录一个城市的信息java

In [1]: from collections import namedtuple

In [2]: City = namedtuple('City', 'name country population coordinates')

In [3]: tokyo = City('Tokyo', 'JP', 36.933, (35.689722, 139.691667))

In [4]: tokyo
Out[4]: City(name='Tokyo', country='JP', population=36.933, coordinates=(35.689722, 139.691667))

In [5]: tokyo.population
Out[5]: 36.933

In [6]: tokyo.coordinates
Out[6]: (35.689722, 139.691667)

In [7]: tokyo[1]
Out[7]: 'JP'
复制代码

建立一个具名元组须要两个参数,一个是类名,另外一个是类的各个字段的名字。后者能够是由数个字符串组成的可迭代对象,或者是由空格分隔开的字段名组成的字符串。python

除了从普通元组那里继承来的属性以外,具名元组还有一些本身专有的属性。c++

In [8]: City._fields
Out[8]: ('name', 'country', 'population', 'coordinates')

In [9]: LatLong = namedtuple('LatLong', 'lat long')

In [10]: delhi_data = ('Delhi NCR', 'IN', 21.935, LatLong(28.613889, 77.208889))

In [11]: delhi = City._make(delhi_data)

In [12]: delhi._asdict()
Out[12]: 
OrderedDict([('name', 'Delhi NCR'),
             ('country', 'IN'),
             ('population', 21.935),
             ('coordinates', LatLong(lat=28.613889, long=77.208889))])

In [13]: for key, value in delhi._asdict().items():
    ...:     print(key + ':', value)
    ...:     
name: Delhi NCR
country: IN
population: 21.935
coordinates: LatLong(lat=28.613889, long=77.208889)

复制代码

_fields 属性是一个包含这个类全部字段名称的元组。swift

_make() 经过接受一个可迭代对象来生成这个类的一个实例,它的做用跟 City(*delhi_data) 是同样的。安全

_asdict() 把具名元组以 collections.OrderedDict 的形式返回,咱们能够利用它来把元组里的信息友好地呈现出来。app

defaultdict

首先咱们看一个例子。函数

用 dict 统计一个 list 中字符串出现的次数:spa

In [1]: langs = ['java', 'php', 'python', 'C#', 'kotlin', 'swift', 'python']

In [2]: res_dict = {}

In [3]: for lang in langs:
   ...:     if lang in res_dict:
   ...:         res_dict[lang] += 1
   ...:     else:
   ...:         res_dict[lang] = 1
   ...: 

In [4]: res_dict
Out[4]: {'C#': 1, 'java': 1, 'kotlin': 1, 'php': 1, 'python': 2, 'swift': 1}

复制代码

这里每次循环都要判断一次,能够调用 setdefault 方法来消除判断。

In [1]: langs = ['java', 'php', 'python', 'C#', 'kotlin', 'swift', 'python']

In [2]: res_dict = {}

In [3]: for lang in langs:
   ...:     res_dict.setdefault(lang, 0)
   ...:     res_dict[lang] += 1
   ...: 

In [4]: res_dict
Out[4]: {'C#': 1, 'java': 1, 'kotlin': 1, 'php': 1, 'python': 2, 'swift': 1}
复制代码

可是如今还有一个错误,每次取值使还要进行一次判断,不然若是值不存在就会抛异常:

In [5]: res_dict['c++']
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-5-269671e9ed5a> in <module>()
----> 1 res_dict['c++']

KeyError: 'c++'

复制代码

有时候为了方便起见,就算某个键在映射里不存在,咱们也但愿在经过这个键读取值的时候能获得一个默认值。有两个途径能帮咱们达到这个目的,一个是经过 defaultdict 这个类型而不是普通的 dict,另外一个是给本身定义一个 dict 的子类,而后在子类中实现 __missing__ 方法。

使用 defaultdict

In [7]: from collections import defaultdict

In [8]: res_dict= defaultdict(int)

In [9]: for lang in langs:
   ...:     res_dict[lang] += 1
   ...: 

In [10]: res_dict
Out[10]: 
defaultdict(int,
            {'C#': 1,
             'java': 1,
             'kotlin': 1,
             'php': 1,
             'python': 2,
             'swift': 1})

In [11]: res_dict['c++']
Out[11]: 0
复制代码

这样就完美解决了上述全部问题, defaultdict 构造函数接收一个可调用的对象,当 __getitem__ 方法找不到值的时候就会调用该对象返回一个值。

因此咱们能够返回更复杂的默认值:

In [25]: def gen_dict():
    ...:     return {'name': 'None', 'age': 0}
    ...: 

In [26]: res_dict = defaultdict(gen_dict)

In [27]: res_dict['zhangsan']
Out[27]: {'age': 0, 'name': 'None'}

复制代码

__missing__ 方法

In [28]: class CustomDict(dict):
    ...:     
    ...:     def __missing__(self, key):
    ...:         return {'name': 'None', 'age': 18}
    ...: 

In [29]: res_dict = CustomDict()

In [30]: res_dict['lisi']
Out[30]: {'age': 18, 'name': 'None'}

复制代码

deque

collections.deque 类(双向队列)是一个线程安全、能够快速从两端添加或者删除元素的数据类型。并且若是想要有一种数据类型来存放“最近用到的几个元素”,deque 也是一个很好的选择。这是由于在新建一个双向队列的时候,你能够指定这个队列的大小,若是这个队列满员了,还能够从反向端删除过时的元素,而后在尾端添加新的元素。

In [1]: from collections import deque

In [2]: dq = deque(range(10), maxlen=10)

In [3]: dq
Out[3]: deque([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

In [4]: dq.rotate(3)

In [5]: dq
Out[5]: deque([7, 8, 9, 0, 1, 2, 3, 4, 5, 6])

In [6]: dq.rotate(-4)

In [7]: dq
Out[7]: deque([1, 2, 3, 4, 5, 6, 7, 8, 9, 0])

In [8]: dq.appendleft(-1)

In [9]: dq
Out[9]: deque([-1, 1, 2, 3, 4, 5, 6, 7, 8, 9])

In [10]: dq.extend([11, 22, 33])

In [11]: dq
Out[11]: deque([3, 4, 5, 6, 7, 8, 9, 11, 22, 33])

In [12]: dq.extendleft([10, 20, 30, 40])

In [13]: dq
Out[13]: deque([40, 30, 20, 10, 3, 4, 5, 6, 7, 8])

复制代码

maxlen 是一个可选参数,表明这个队列能够容纳的元素的数量,并且一旦设定,这个属性就不能修改了。

队列的旋转操做 (rotate) 接受一个参数 n,当 n > 0 时,队列的最右边的 n 个元素会被移动到队列的左边。当 n < 0 时,最左边的 n 个元素会被移动到右边。

当试图对一个已满(len(d) == d.maxlen)的队列作尾部添加操做的时候,它头部的元素会被删除掉。

extendleft(iter) 方法会把迭代器里的元素逐个添加到双向队列的左边,所以迭代器里的元素会逆序出如今队列里。

Counter

这个映射类型会给键准备一个整数计数器。每次更新一个键的时候都会增长这个计数器。因此这个类型能够用来给可散列表对象计数,或者是当成多重集来用——多重集合就是集合里的元素能够出现不止一次。Counter 实现了 + 和 - 运算符用来合并记录,还有像 most_common([n]) 这类颇有用的方法。most_common([n]) 会按照次序返回映射里最多见的 n 个键和它们的计数

In [1]: from collections import Counter

In [2]: langs = ['java', 'php', 'python', 'C#', 'kotlin', 'swift', 'python']

In [3]: ct = Counter(langs)

In [4]: ct
Out[4]: Counter({'C#': 1, 'java': 1, 'kotlin': 1, 'php': 1, 'python': 2, 'swift': 1})

In [5]: ct.update(['java', 'c'])

In [6]: ct
Out[6]: 
Counter({'C#': 1,
         'c': 1,
         'java': 2,
         'kotlin': 1,
         'php': 1,
         'python': 2,
         'swift': 1})

In [7]: ct.most_common(2)
Out[7]: [('java', 2), ('python', 2)]
复制代码

固然,也能够直接操做字符串:

In [9]: ct = Counter('abracadabra')

In [10]: ct
Out[10]: Counter({'a': 5, 'b': 2, 'c': 1, 'd': 1, 'r': 2})

In [11]: ct.update('aaaaazzz')

In [12]: ct
Out[12]: Counter({'a': 10, 'b': 2, 'c': 1, 'd': 1, 'r': 2, 'z': 3})

In [13]: ct.most_common(2)
Out[13]: [('a', 10), ('z', 3)]

复制代码

OrderedDict

这个类型在添加键的时候会保持顺序,所以键的迭代次序老是一致的。OrderedDict 的 popitem 方法默认删除并返回的是字典里的最后一个元素,可是若是像 my_odict.popitem(last=False) 这样调用它,那么它删除并返回第一个被添加进去的元素。

move_to_end(key, last=True) 将现有 key 移至有序字典的末尾。若是 last=True(默认),则 item 移动到右侧,若是 last=False,则移动到开始。若是 key 不存在,则引起 KeyError

In [1]: from collections import OrderedDict

In [2]: d = OrderedDict.fromkeys('abcde')

In [3]: d.move_to_end('b')

In [4]: ''.join(d.keys())
Out[4]: 'acdeb'

In [5]: d.move_to_end('b', last=False)

In [6]: ''.join(d.keys())
Out[6]: 'bacde'

复制代码

因为 OrderedDict 会记住它的插入顺序,所以它能够与 sorted 结合使用来建立一个排序后的字典:

In [11]: d = {'banana': 3, 'apple': 4, 'pear': 1, 'orange': 2}
# 根据 key 排序
In [12]: OrderedDict(sorted(d.items(), key=lambda t:t[0]))
Out[12]: OrderedDict([('apple', 4), ('banana', 3), ('orange', 2), ('pear', 1)])
# 根据 value 排序
In [13]: OrderedDict(sorted(d.items(), key=lambda t:t[1]))
Out[13]: OrderedDict([('pear', 1), ('orange', 2), ('banana', 3), ('apple', 4)])
# 根据 key 的长度排序
In [14]: OrderedDict(sorted(d.items(), key=lambda t: len(t[0])))
Out[14]: OrderedDict([('pear', 1), ('apple', 4), ('banana', 3), ('orange', 2)])

复制代码

删除条目时,新排序的字典会保持排序顺序。可是,当添加新的 key 时,key 被追加到最后,并不保持排序。

ChainMap

ChainMap 类提供用于快速连接多个 dict,以便将它们视为单个单元。它一般比建立新 dict 和运行多个 update() 调用要快得多。

In [1]: from collections import ChainMap

In [2]: d1 = {'java': 3, 'python': 4}

In [3]: d2 = {'c++': 1, 'java': 2}

In [4]: for key, val in ChainMap(d1, d2).items():
   ...:     print(key, val)
   ...:     
c++ 1
java 3
python 4

复制代码

后出现的重复的 key 将被忽略

ChainMap 将连接的 dict 存储在一个列表中。该列表是公开的,可使用 maps 属性进行访问或更新。

In [10]: c1 = ChainMap(d1, d2)

In [11]: c1.maps[0]
Out[11]: {'java': 3, 'python': 4}

In [12]: c1.maps[0]['python'] = 2

In [13]: c1.items()
Out[13]: ItemsView(ChainMap({'java': 3, 'python': 2}, {'c++': 1, 'java': 2}))

In [14]: dict(c1)
Out[14]: {'c++': 1, 'java': 3, 'python': 2}

复制代码

参考

python必学模块-collections
8.3. collections — Container datatypes 《流畅的 Python》相关章节

相关文章
相关标签/搜索