collections

我们知道,Python 的数据类型有 list, tuple, dict, str 等,collections 模块提供了额外 5 个高性能的数据类型:

  • Counter: 计数器
  • OrderedDict: 有序字典
  • defaultdict: 带有默认值的字典
  • namedtuple: 生成可以通过属性访问元素内容的 tuple 子类
  • deque: 双端队列,能够在队列两端添加或删除元素

Counter

Counter 是一个简单的计数器,可用于统计字符串、列表等的元素个数。

看看例子:

  1. >>> from collections import Counter
  2. >>>
  3. >>> s = 'aaaabbbccd'
  4. >>> c = Counter(s) # 创建了一个 Counter 对象
  5. >>> c
  6. Counter({'a': 4, 'b': 3, 'c': 2, 'd': 1})
  7. >>> isinstance(c, dict) # c 其实也是一个字典对象
  8. True
  9. >>> c.get('a')
  10. 4
  11. >>> c.most_common(2) # 获取出现次数最多的前两个元素
  12. [('a', 4), ('b', 3)]

在上面,我们使用 Counter() 创建了一个 Counter 对象 cCounter 其实是 dict 的一个子类,我们可以使用 get 方法来获取某个元素的个数。Counter 对象有一个 most_common 方法,允许我们获取出现次数最多的前几个元素。

另外,两个 Counter 对象还可以做运算:

  1. >>> from collections import Counter
  2. >>>
  3. >>> s1 = 'aaaabbbccd'
  4. >>> c1 = Counter(s1)
  5. >>> c1
  6. Counter({'a': 4, 'b': 3, 'c': 2, 'd': 1})
  7. >>>
  8. >>> s2 = 'aaabbef'
  9. >>> c2 = Counter(s2)
  10. >>> c2
  11. Counter({'a': 3, 'b': 2, 'e': 1, 'f': 1})
  12. >>>
  13. >>> c1 + c2 # 两个计数结果相加
  14. Counter({'a': 7, 'b': 5, 'c': 2, 'e': 1, 'd': 1, 'f': 1})
  15. >>> c1 - c2 # c2 相对于 c1 的差集
  16. Counter({'c': 2, 'a': 1, 'b': 1, 'd': 1})
  17. >>> c1 & c2 # c1 和 c2 的交集
  18. Counter({'a': 3, 'b': 2})
  19. >>> c1 | c2 # c1 和 c2 的并集
  20. Counter({'a': 4, 'b': 3, 'c': 2, 'e': 1, 'd': 1, 'f': 1})

OrderedDict

Python 中的 dict 是无序的:

  1. >>> dict([('a', 10), ('b', 20), ('c', 15)])
  2. {'a': 10, 'c': 15, 'b': 20}

有时,我们希望保持 key 的顺序,这时可以用 OrderedDict:

  1. >>> from collections import OrderedDict
  2. >>> OrderedDict([('a', 10), ('b', 20), ('c', 15)])
  3. OrderedDict([('a', 10), ('b', 20), ('c', 15)])

defaultdict

在 Python 中使用 dict 时,如果访问了不存在的 key,会抛出 KeyError 异常,因此,在访问之前,我们经常需要对 key 作判断,比如:

  1. >>> d = dict()
  2. >>> s = 'aaabbc'
  3. >>> for char in s:
  4. ... if char in d:
  5. ... d[char] += 1
  6. ... else:
  7. ... d[char] = 1
  8. ...
  9. >>> d
  10. {'a': 3, 'c': 1, 'b': 2}

使用 defaultdict,我们可以给字典中的 key 提供一个默认值。访问 defaultdict 中的 key,如果 key 存在,就返回 key 对应的 value,如果 key 不存在,就返回默认值。

  1. >>> from collections import defaultdict
  2. >>> d = defaultdict(int) # 默认的 value 值是 0
  3. >>> s = 'aaabbc'
  4. >>> for char in s:
  5. ... d[char] += 1
  6. ...
  7. >>> d
  8. defaultdict(<type 'int'>, {'a': 3, 'c': 1, 'b': 2})
  9. >>> d.get('a')
  10. 3
  11. >>> d['z']
  12. 0

使用 defaultdict 时,我们可以传入一个工厂方法来指定默认值,比如传入 int,表示默认值是 0,传入 list,表示默认是 []

  1. >>> from collections import defaultdict
  2. >>>
  3. >>> d1 = defaultdict(int)
  4. >>> d1['a']
  5. 0
  6. >>> d2 = defaultdict(list)
  7. >>> d2['b']
  8. []
  9. >>> d3 = defaultdict(str)
  10. >>> d3['a']
  11. ''

我们还可以自定义默认值,通过 lambda 函数来实现:

  1. >>> from collections import defaultdict
  2. >>>
  3. >>> d = defaultdict(lambda: 10)
  4. >>> d['a']
  5. 10

namedtuple

我们经常用 tuple (元组) 来表示一个不可变对象,比如用一个 (姓名, 学号, 年龄) 的元组来表示一个学生:

  1. >>> stu = ('ethan', '001', 20)
  2. >>> stu[0]
  3. 'ethan'

这里使用 tuple 没什么问题,但可读性比较差,我们必须清楚索引代表的含义,比如索引 0 表示姓名,索引 1 表示学号。如果用类来定义,就可以通过设置属性 name, id, age 来表示,但就有些小题大作了。

我们可以通过 namedtuple 为元组的每个索引设置名称,然后通过「属性名」来访问:

  1. >>> from collections import namedtuple
  2. >>> Student = namedtuple('Student', ['name', 'id', 'age']) # 定义了一个 Student 元组
  3. >>>
  4. >>> stu = Student('ethan', '001', 20)
  5. >>> stu.name
  6. 'ethan'
  7. >>> stu.id
  8. '001'

deque

deque 是双端队列,允许我们在队列两端添加或删除元素。

  1. >>> from collections import deque
  2. >>> q = deque(['a', 'b', 'c', 'd'])
  3. >>> q.append('e') # 添加到尾部
  4. >>> q
  5. deque(['a', 'b', 'c', 'd', 'e'])
  6. >>> q.appendleft('o') # 添加到头部
  7. >>> q
  8. deque(['o', 'a', 'b', 'c', 'd', 'e'])
  9. >>> q.pop() # 从尾部弹出元素
  10. 'e'
  11. >>> q
  12. deque(['o', 'a', 'b', 'c', 'd'])
  13. >>> q.popleft() # 从头部弹出元素
  14. 'o'
  15. >>> q
  16. deque(['a', 'b', 'c', 'd'])
  17. >>> q.extend('ef') # 在尾部 extend 元素
  18. >>> q
  19. deque(['a', 'b', 'c', 'd', 'e', 'f'])
  20. >>> q.extendleft('uv') # 在头部 extend 元素,注意顺序
  21. >>> q
  22. deque(['v', 'u', 'a', 'b', 'c', 'd', 'e', 'f'])
  23. >>>
  24. >>> q.rotate(2) # 将尾部的两个元素移动到头部
  25. >>> q
  26. deque(['e', 'f', 'v', 'u', 'a', 'b', 'c', 'd'])
  27. >>> q.rotate(-2) # 将头部的两个元素移动到尾部
  28. >>> q
  29. deque(['v', 'u', 'a', 'b', 'c', 'd', 'e', 'f'])

其中,rotate 方法用于旋转,如果旋转参数 n 大于 0,表示将队列右端的 n 个元素移动到左端,否则相反。

参考资料