Python的collections模块中的defaultdict真的比使用setdefault更快吗?

20

我看到其他Python程序员在以下情况下使用collections模块中的defaultdict:

from collections import defaultdict

s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]

def main():
    d = defaultdict(list)
    for k, v in s:
        d[k].append(v)

我通常使用setdefault方法来解决这个问题:

def main():
    d = {}
    for k, v in s:
        d.setdefault(k, []).append(v)

文档确实声称使用defaultdict更快, 但是在我自己测试时,我发现相反的情况:

$ python -mtimeit -s "from withsetdefault import main; s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)];" "main()"
100000 loops, best of 3: 4.51 usec per loop
$ python -mtimeit -s "from withdefaultdict import main; s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)];" "main()"
100000 loops, best of 3: 5.38 usec per loop

我设置的测试有问题吗?

参考资料,我正在使用Python 2.7.3 [GCC 4.2.1(Apple Inc. build 5666)


一个测试文件中是否包含s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)],而另一个文件没有? - Claudiu
@Claudiu:仔细阅读timeit行。 - Martijn Pieters
4
请使用更大的测试集,其中每个键具有超过2个值。 - Martijn Pieters
@MartijnPieters:我看了。我不知道withsetdefault.pywithdefaultdict.py里面有什么。如果其中一个包含那行代码而另一个没有,那就可以解释这个差异了。 - Claudiu
1个回答

25

没错,有一些“问题”:

你把创建(默认)字典的代码放在语句中而不是设置中。构造一个新的defaultdict比构造普通dict更昂贵,通常这不是在程序中进行性能分析时应该优化的瓶颈 - 毕竟,你只需要构建数据结构一次,但却会使用多次。

如果你像下面这样进行测试,你会发现defaultdict的操作确实更快:

>>> import timeit
>>> setup1 = """from collections import defaultdict
... s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
... d = defaultdict(list)"""
>>> stmt1 = """for k, v in s:
...     d[k].append(v)"""
>>> setup2 = """s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
... d = {}"""
>>> stmt2 = """for k, v in s:
...     d.setdefault(k, []).append(v)"""
>>> timeit.timeit(setup=setup1, stmt=stmt1)
1.0283400125194078
>>> timeit.timeit(setup=setup2, stmt=stmt2)
1.7767367580925395

在Win7 x64上运行的Python 2.7.3。


谢谢Tim!当我从测试语句中删除defaultdict构造函数时,无论数据大小如何,它肯定比使用setdefault更快。 - damzam
你只谈到构建(默认)字典是不同的地方,但我认为你还有第二个可能很重要的区别:在原始版本中,60%的所有键都是新的,需要插入并使用[]。在你的版本中,由于每个片段重复了一百万次,这几乎是0%。 - superb rain

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接