我用timeit获得了非常令人惊讶的结果,有人可以告诉我,如果我做错了吗?我使用的是Python 2.7。
这是文件speedtest_init.py的内容:
import random
to_count = [random.randint(0, 100) for r in range(60)]
这些是speedtest.py的内容:
__author__ = 'BlueTrin'
import timeit
def test_init1():
print(timeit.timeit('import speedtest_init'))
def test_counter1():
s = """\
d = defaultdict(int);
for i in speedtest_init.to_count:
d[i] += 1
"""
print(timeit.timeit(s, 'from collections import defaultdict; import speedtest_init;'))
def test_counter2():
print(timeit.timeit('d = Counter(speedtest_init.to_count);', 'from collections import Counter; import speedtest_init;'))
if __name__ == "__main__":
test_init1()
test_counter1()
test_counter2()
控制台输出是:
C:\Python27\python.exe C:/Dev/codility/chlorum2014/speedtest.py
2.71501962931
65.7090444503
91.2953839048
Process finished with exit code 0
我认为默认情况下timeit()运行代码的1000000倍,所以我需要将时间除以1000000,但令人惊讶的是Counter慢于defaultdict()。
这是预期的吗?
编辑:
同样使用dict比defaultdict(int)更快:
def test_counter3():
s = """\
d = {};
for i in speedtest_init.to_count:
if i not in d:
d[i] = 1
else:
d[i] += 1
"""
print(timeit.timeit(stmt=s, setup='from collections import defaultdict; import speedtest_init;')
这最后一个版本比defaultdict(int)更快,这意味着除非你更关心可读性,否则你应该使用dict()而不是defaultdict()。
答案 0 :(得分:15)
是的,这是预期的; Counter()
构造函数使用Counter.update()
使用self.get()
来加载初始值,而不是依赖__missing__
。
此外,defaultdict
__missing__
工厂完全使用C代码处理,尤其是在使用其他类型的int()
时,它本身是用C实现的。Counter
来源是纯Python,因此Counter.__missing__
方法需要执行Python框架。
由于仍然在C中处理dict.get()
,因此构造函数方法是Counter()
的更快方法,前提是您使用相同的技巧Counter.update()
使用并为self.get
添加别名首先查找本地:
>>> import timeit
>>> import random
>>> 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
defaultdict
和Counter
都是为其功能而非其性能而构建的有用类;不依赖__missing__
钩子可以更快:
>>> 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()
方法获得最大速度的常规字典。但是,您还必须重新实现Counter
或Counter.most_common()
方法的行李行为。 defaultdict
用例超出了计数范围。
在Python 3.2中,通过添加处理此案例的C库来更新Counter()
可以提高速度;见issue 10667。在Python 3.4上进行测试,Counter()
构造函数现在胜过别名dict.get
案例:
>>> 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