是的,这是预期的;这Counter()
构造函数 uses Counter.update()
它使用self.get()
加载初始值而不是依赖__missing__
.
此外,defaultdict
__missing__
工厂完全用 C 代码处理,特别是在使用另一种类型时,例如int()
它本身是用 C 实现的。Counter
源是纯Python,因此Counter.__missing__
方法需要Python框架才能执行。
Because dict.get()
仍然用 C 处理,构造函数方法是更快的方法Counter()
,只要你使用同样的技巧Counter.update()
使用和别名self.get
首先作为本地查找:
>>> import timeit
>>> import random
>>> import sys
>>> sys.version_info
sys.version_info(major=2, minor=7, micro=9, releaselevel='final', serial=0)
>>> to_count = [random.randint(0, 100) for r in range(60)]
>>> timeit.timeit('for i in to_count: c[i] += 1',
... 'from collections import Counter; from __main__ import to_count; c = Counter()',
... number=10000)
0.2510359287261963
>>> timeit.timeit('for i in to_count: c[i] = c_get(i, 0) + 1',
... 'from collections import Counter; from __main__ import to_count; c = Counter(); c_get = c.get',
... number=10000)
0.20978617668151855
Both defaultdict
and Counter
是为功能而非性能而构建的有用类;不依赖于__missing__
hook 还可以更快:
>>> timeit.timeit('for i in to_count: d[i] = d_get(i, 0) + 1',
... 'from __main__ import to_count; d = {}; d_get = d.get',
... number=10000)
0.11437392234802246
这是一个使用别名的常规字典dict.get()
最大速度的方法。但随后你还必须重新实现 bag 行为Counter
, 或者Counter.most_common()
方法。这defaultdict
用例远远超出了计算的范围。
在 Python 3.2 中,更新了Counter()
通过添加处理这种情况的 C 库,速度得到了提升;看问题 10667。在 Python 3.4 上进行测试Counter()
构造函数现在击败了别名dict.get
case:
>>> timeit.timeit('Counter(to_count)',
... 'from collections import Counter; from __main__ import to_count',
... number=100000)
0.8332311600097455
>>> timeit.timeit('for i in to_count: d[i] = d_get(i, 0) + 1',
... 'from __main__ import to_count; d = {}; d_get = d.get',
... number=100000)
0.961191965994658
>>> import sys
>>> sys.version_info
sys.version_info(major=3, minor=4, micro=2, releaselevel='final', serial=0)
(注意:为了获得有意义的计时结果,迭代次数从 10k 增加到 100k;因此,如果将这些与dict.get()
在上面的情况下,您需要将计时时间乘以 10,即 1.144 秒)。