填充列表与词典时的​​性能注意事项

时间:2014-10-15 15:49:36

标签: python performance iteration containers

假设我需要在迭代中收集数百万个字符串,以后我可以按位置随机索引。

我需要按顺序填充可迭代的一个项目,数百万个条目。

鉴于上述情况,原则上哪种方法可以更有效:

填充列表:

while <condition>:
  if <condition>:
     my_list[count] = value
     count += 1

填充字典:

while <condition>:
  if <condition>:
     my_dict[count] = value
     count += 1

(以上是pesudocode,在运行代码片段之前,所有内容都会被初始化)。

我对Python 3.4的CPython实现特别感兴趣。

3 个答案:

答案 0 :(得分:3)

如果以正确的方式使用它们,列表肯定会更快。

In [19]: %%timeit l = []
   ....: for i in range(1000000): l.append(str(i))
   ....: 
1 loops, best of 3: 182 ms per loop

In [20]: %%timeit d = {}
   ....: for i in range(1000000): d[i] = str(i)
   ....: 
1 loops, best of 3: 207 ms per loop

In [21]: %timeit [str(i) for i in range(1000000)]
10 loops, best of 3: 158 ms per loop

通过理解将Python循环推向C级会给你带来相当多的时间。更喜欢将列表作为整数前缀的键更有意义。预分配可以节省更多时间:

>>> %%timeit
... l = [None] * 1000000
... for i in xrange(1000000): my_list[i] = str(i)
... 
10 loops, best of 3: 147 ms per loop

为了完整性,词典理解加快速度:

In [22]: %timeit {i: str(i) for i in range(1000000)}
1 loops, best of 3: 213 ms per loop

对于较大的字符串,我发现性能上存在非常相似的差异(尝试str(i) * 10)。这是x86-64上的CPython 2.7.6。

答案 1 :(得分:1)

我不明白你为什么要创建一个空列表或字典,然后填充它。为什么不直接从生成过程创建新的列表或字典?

results = list(a_generator)

# Or if you really want to use a dict for some reason:
results = dict(enumerate(a_generator))

答案 2 :(得分:1)

使用map功能可以获得更好的时间:

>>> def test1():
    l = []
    for i in range(10 ** 6):
        l.append(str(i))

>>> def test2():
    d = {}
    for i in range(10 ** 6):
        d[i] = str(i)

>>> def test3():
    [str(i) for i in range(10 ** 6)]

>>> def test4():
    {i: str(i) for i in range(10 ** 6)}

>>> def test5():
    list(map(str, range(10 ** 6)))

>>> def test6():
    r = range(10 ** 6)
    dict(zip(r, map(str, r)))

>>> timeit.Timer('test1()', 'from __main__ import test1').timeit(100)
30.628035710889932
>>> timeit.Timer('test2()', 'from __main__ import test2').timeit(100)
31.093550469839613
>>> timeit.Timer('test3()', 'from __main__ import test3').timeit(100)
25.778271498509355
>>> timeit.Timer('test4()', 'from __main__ import test4').timeit(100)
30.10892986559668
>>> timeit.Timer('test5()', 'from __main__ import test5').timeit(100)
20.633583353028826
>>> timeit.Timer('test6()', 'from __main__ import test6').timeit(100)
28.660790917067914