Python 高手编程系列一百九十四:使用集合模块

collections 模块提供了高性能的容器类型,它可以替换内置的容器类型。此模块
中可用的主要类型有。
• deque:具有额外特性的类列表类型。
• defaultdict:具有内置默认工厂特性的类字典类型。
• namedtuple:为成员分配键的类元组类型。
deque
deque 是列表的替代实现。列表基于数组,而 deque 基于双向链表。因此,当你需
要在它的中间或头部插入一些东西时,deque 会快得多。但是当你需要进行随机访问时,
它就会比较慢。
当然,在 Python 中,由于列表类型中内部数组的过度分配,不是每个 list.append()
调用都需要内存重分配,所以这个方法的平均复杂度是 O(1)。不过,当在链表而不是数组
上执行时,弹出和追加通常更快。当元素需要添加到序列的任意点时,情况会发生巨大变
化。因为新元素右边的所有元素都需要在数组中移动,list.insert()的复杂度就是
O(n)。如果你需要执行大量的弹出、追加和插入,替代列表的 deque 可以提供实质性的性
能提升。但是在从列表切换到 deque 之前,你应该总是确保分析你的代码,因为在数组中
快速的一些事情(例如随机访问)在链表中却是非常低效。
例如,如果我们使用 timeit 测量追加一个元素并从序列中删除它的时间,list 和
deque 之间的差异甚至可能不明显,如下所示:
$ python3 -m timeit \

-s ‘sequence=list(range(10))’
‘sequence.append(0); sequence.pop();’
1000000 loops, best of 3: 0.168 usec per loop
$ python3 -m timeit
-s ‘from collections import deque; sequence=deque(range(10))’
‘sequence.append(0); sequence.pop();’
1000000 loops, best of 3: 0.168 usec per loop
但是,当我们想添加和删除序列的第一个元素时,如果我们对这种情况进行类似的比
较,性能差异是令人印象深刻的,如下所示:
$ python3 -m timeit
-s ‘sequence=list(range(10))’
‘sequence.insert(0, 0); sequence.pop(0)’
1000000 loops, best of 3: 0.392 usec per loop
$ python3 -m timeit
-s ‘from collections import deque; sequence=deque(range(10))’
‘sequence.appendleft(0); sequence.popleft()’
10000000 loops, best of 3: 0.172 usec per loop
并且当序列的大小增长时,差异就变得更大。在包含 10,000 个元素的列表上执行的相
同测试,示例如下:
$ python3 -m timeit
-s ‘sequence=list(range(10000))’
‘sequence.insert(0, 0); sequence.pop(0)’
100000 loops, best of 3: 14 usec per loop
$ python3 -m timeit
-s ‘from collections import deque; sequence=deque(range(10000))’
‘sequence.appendleft(0); sequence.popleft()’
10000000 loops, best of 3: 0.168 usec per loop
由于有效的 append()和 pop()方法,从序列的两端以相同的速度工作,deque 是一
个实现队列的完美类型。例如,如果使用 deque 而不是 list 实现,FIFO(先进先出)
队列肯定会更加高效。
defaultdict
defaultdict 类型类似于字典类型,但为新键添加了默认工厂。这避免了编写额外
的测试来初始化映射实体,并且比 dict.setdefault 方法更加高效。
defaultdict 似乎就像 dict 之上的语法糖,只是允许你写较短的代码。事实上,
在失败的键查找上回退到预定义的值也比 dict.setdefault()方法稍快,如下所示:
$ python3 -m timeit
-s ‘d = {}’
‘d.setdefault(“x”, None)’
10000000 loops, best of 3: 0.153 usec per loop
$ python3 -m timeit
-s ‘from collections import defaultdict; d=defaultdict(lambda: None)’
‘d[“x”]’
10000000 loops, best of 3: 0.0447 usec per loop
差别不大,因为计算复杂度没有改变。dict.setdefault 方法包括两个步骤(键查找和键设置),它们都具有 O(1)的复杂度,我们已经在第 2 章中的字典部分中了解过。没有
复杂度低于 O(1)的类。但是在某些情况下它是毋庸置疑的更快,它值得你去了解,因为在
优化关键代码段时,每一个小的速度提升都非常有意义。
defaultdict 类型使用工厂作为参数,因此可以与内置类型或者构造函数不接受参
数的类一起使用。下面是官方文档中的一个例子,显示了如何使用 defaultdict 进行
计数,如下所示:

s = ‘mississippi’
d = defaultdict(int)
for k in s:
… d[k] += 1

list(d.items())
[(‘i’, 4), (‘p’, 2), (‘s’, 4), (‘m’, 1)]

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值