Python 标准库 collections

参考:

Python 的内置数据数据类型包括 str, int, list, tuple, set, dict 等,有时这些数据类型满足不了我们的需求。不过标准库的 collections 模块在这些内置数据类型的基础上,提供了几个额外的数据类型:

  • namedtuple 命名元组,使用名字访问元素 New in version 2.6.
  • deque 双端队列,可以快速的从头/尾两端添加或删除元素 New in version 2.4.
  • Counter 计数器,用于对某项数据进行计数 New in version 2.7.
  • OrderedDict 有序字典,保持插入顺序 New in version 2.7.
  • defaultdict 带有默认值的字典 New in version 2.5.
  • ChainMap 合并多个 map(dict),但保持原数据结构 New in version 3.3
  • UserDict 将字典包装起来使得创建字典的子类更容易
  • UserList 列表对象的包装器
  • UserString 字符串对象的包装器

namedtuple

可以使用名称来访问元素的数据对象,返回一个 带 name fields 的子类

collections.namedtuple(typename, field_names, *, verbose=False, rename=False, module=None)

field_names 用于指定数据对象的元素,可以是一个 str,例如 'x y' 或者 'x, y',也可以是一个包含字符串的序列类型,像 ['x', 'y']

常用方法

  • _asdict() 将 namedtuple 转换为一个 OrderedDict
  • _fields() 返回 name fields 中的 key
  • _replace() 类似于 str 的 replace 方法
  • _make() 通过一个序列或可迭代对象创建
>>> Point = namedtuple('Point', ['x', 'y', 'z'])
>>> p = Point(10, 11, 12)
>>> p
Point(x=10, y=11, z=12)
>>> p.x
10
>>> p.x + p.y + p.z
33
>>> p2 = Point(11, y=22, z=33)
>>> p2
Point(x=11, y=22, z=33)
>>> d = p2._asdict()      # 转换为字典
>>> d
OrderedDict([('x', 11), ('y', 22), ('z', 33)])
>>> p2._replace(y=100)    # 替换元素值
Point(x=11, y=100, z=33)
>>> p._fields             # 查看对象字段
('x', 'y', 'z')
>>> Point._make(range(3)) # 通过一个序列或者可迭代对象创建一个对象
Point(x=0, y=1, z=2

deque

deque 是 double-ended queue 的缩写,即双端队列。List 存储数据的优势在于按索引查找元素会很快,但是插入和删除元素就很慢了,因为 list 是基于数组实现的。deque 是为了高效实现插入和删除操作的双向列表,list存储数据的优势在于按索引查找元素会很快,但是插入和删除元素就很慢了,因为list是基于数组实现的。deque是为了高效实现插入和删除操作的双向列表,适合用于队列和栈,而且线程安全。

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

deque 具有 list 的所有方法,此外还有 appendleft/popleft ,插入元素的复杂度 O(1), 而 list 是 O(n)

maxlen 用于指定 deque 的最大长度,当 deque 的长度达到最大时,先加入的元素会按加入顺序被移出 deque ,如果不指定 maxlen 则为无限长。

常用方法

  • append
  • extend
  • appendleft
  • appendright
  • popleft
  • popright
  • rotate deque 内元素按顺序移动一位,最后一位移动到第一位
>>> dq = deque()
>>> dq
deque([])
>>> dq.extend([1, 2, 3])
>>> dq.extend([1, 2, 3])
>>> dq
deque([1, 2, 3, 1, 2, 3])
>>> dq = deque(maxlen=2)
>>> dq
deque([], maxlen=2)
>>> dq.append(1)
>>> dq.append(1)
>>> dq
deque([1, 1], maxlen=2)
>>> dq.append(3)
>>> dq
deque([1, 3], maxlen=2)
>>> dq.append(4)
>>> dq
deque([3, 4], maxlen=2)
>>> dq.appendleft(5)
>>> dq
deque([5, 3], maxlen=2

跑马灯程序

import sys
import time
from collections import deque

fancy_loading = deque('>--------------------')

while True:
    print('\r%s' % ''.join(fancy_loading),)
    fancy_loading.rotate(1)
    sys.stdout.flush()
    time.sleep(0.08)

Counter

Counter 用来统计相关元素的出现次数,返回一个 Counter 对象,类似于字典

collections.Counter([iterable-or-mapping])

常用方法

  • update 追加元素
  • most_common(n) 获取出现次数最多的 n 个元素
>>> c = Counter('abracadabra')
>>> c
Counter({'a': 5, 'r': 2, 'b': 2, 'd': 1, 'c': 1})
>>> c.update('zzzbbe')  # 追加元素
>>> c
Counter({'a': 5, 'b': 4, 'z': 3, 'r': 2, 'e': 1, 'c': 1, 'd': 1})
>>> c.most_common(3)  # 获取出现频率最高的前 3 个字符
[('a', 5), ('b', 4), ('z', 3)]

OrderedDict

OrderedDict 是 dict 的一个子类,支持所有 dict 的方法,保持 dict 的有序性

collections.OrderedDict([items])

常用方法

  • popitem(last = True)
  • move_to_end(key, last = True) 以上两个方法的 last 关键字参数都是控制从头还是尾弹出对象的
>>> d = OrderedDict({'banana': 3, 'apple': 4, 'pear': 1, 'orange': 2})
>>> d
OrderedDict([('banana', 3), ('apple', 4), ('pear', 1), ('orange', 2)])
>>> d['cherry'] = 8
>>> d
OrderedDict([('banana', 3), ('apple', 4), ('pear', 1), ('orange', 2), ('cherry', 8)])
>>> for k, v in d.items():
...     print(k, v)
...
...
banana 3
apple 4
pear 1
orange 2
cherry 8
>>> d.keys()
odict_keys(['banana', 'apple', 'pear', 'orange', 'cherry'])
>>> d.pop('apple')
4
>>> d
OrderedDict([('banana', 3), ('pear', 1), ('orange', 2), ('cherry', 8)])

defaultdict

在普通的 dict 之上添加了 default_factory,使得 key 不存在时会自动生成相应类型的 value,default_factory 参数可以指定成 list, set, int 等各种合法类型

作用类似于 dict 中的 set_default 方法,代码更优雅一些

collections.defaultdict([default_factory[, …]])
>>> d = defaultdict(lambda: None)
>>> d
defaultdict(<function <lambda> at 0x1115ef620>, {})
>>> d['a']
>>> print(d['b'])
None
>>> d
defaultdict(<function <lambda> at 0x1115ef620>, {'a': None, 'b': None})
>>> d = defaultdict(int)
>>> d["hello"]
0
>>> d
defaultdict(<class 'int'>, {'hello': 0})

ChainMap

合成多个 dict 类似于 update 方法,只不过不是就地实现,且效率更高

collections.ChainMap(*maps)
>>> dict1 = { 'a' : 1, 'b' : 2 }
>>> dict2 = { 'b' : 3, 'c' : 4 }
>>> chain = ChainMap(dict1, dict2)
>>> chain
ChainMap({'a': 1, 'b': 2}, {'b': 3, 'c': 4})
>>> chain.maps
[{'a': 1, 'b': 2}, {'b': 3, 'c': 4}]
>>> chain.keys()
KeysView(ChainMap({'a': 1, 'b': 2}, {'b': 3, 'c': 4}))
>>> list(chain.keys())
['a', 'b', 'c']
>>> chain['b']  # 获取的是第一个字典中的值
2
>>> chain['a']
1
>>> chain['c']
4
>>> chain['d'] = 5
>>> chain['e'] = 6  # 被添加到了第一个字典中
>>> chain
ChainMap({'a': 1, 'd': 5, 'b': 2, 'e': 6}, {'b': 3, 'c': 4})
>>> chain['b'] = 8  # 修改了第一个字典中的值
>>> chain
ChainMap({'a': 1, 'd': 5, 'b': 8, 'e': 6}, {'b': 3, 'c': 4})
>>> m = chain.new_child()  # 复制一个 ChainMap 对象
>>> m['g'] = 10
>>> m
ChainMap({'g': 10}, {'a': 1, 'd': 5, 'b': 8, 'e': 6}, {'b': 3, 'c': 4})
>>> chain
ChainMap({'a': 1, 'd': 5, 'b': 8, 'e': 6}, {'b': 3, 'c': 4})
>>> dict3 = { 'h' : 5 }
>>> new_chain = chain.new_child(dict3)  # 添加新字典
>>> new_chain
ChainMap({'h': 5}, {'a': 1, 'd': 5, 'b': 8, 'e': 6}, {'b': 3, 'c': 4})

有时用 ChainMap 替代 update 是个不错的选择,但是 ChainMap 的绝大多数方法都只是在第一个字典上进行操作

UserDict UserList UserString

这三个类是分别对 dict、list、str 三种数据类型的包装,其主要是为方便用户实现自己的数据类型。在 Python2 之前,这三个类分别位于 UserDict、UserList、UserString 三个模块中,需要用类似于 from UserDict import UserDict 的方式导入。在 Python3 之后则被挪到了 collections 模块中。这三个类都是基类,如果用户要扩展这三种类型,只需继承这三个类即可。