Python列表理解与生成器

时间:2016-06-04 00:02:57

标签: python

我发现了这个问题Generators vs List Comprehension performance in Python而不是cProfile我使用timeit。

from timeit import timeit
import cProfile

print timeit('sum([i for i in range(9999999)])', number=1)
print timeit('sum((i for i in range(9999999)))', number=1)

print cProfile.run('sum([i for i in xrange(9999999)])')
print cProfile.run('sum((i for i in xrange(9999999)))')

结果是

LC timeit 0.728941202164
G timeit 0.643975019455
LC cProfile          3 function calls in 0.751 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.673    0.673    0.751    0.751 <string>:1(<module>)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        1    0.078    0.078    0.078    0.078 {sum}


None
G cProfile          10000003 function calls in 1.644 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
 10000000    0.843    0.000    0.843    0.000 <string>:1(<genexpr>)
        1    0.000    0.000    1.644    1.644 <string>:1(<module>)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        1    0.801    0.801    1.644    1.644 {sum}

我相信发电机应该优于列表理解,但为什么在这种情况下结果不明确。我的问题是哪一个更好写

sum((i for i in list_of_i))   # Which use 1 loop

VS

sum([i for i in list_of_i])   # Which seem to took 2 loop: 1 for list create and one for sum

3 个答案:

答案 0 :(得分:4)

在简单的情况下,如果没有理解/生成器,这将是最快的:

sum(xrange(9999999))

通常,如果我需要进行某种操作,我需要在理解和生成器表达式之间进行选择,我会这样做:

sum(a*b for a, b in zip(c, d))

就我个人而言,我认为生成器表达式(没有额外的括号 1 看起来更好,因为可读性很重要 - 这超过了两个表达式之间的任何微观性能差异

生成器对于这样的事情通常会更快,因为它们避免创建中间列表(以及与之关联的内存分配)。随着内存分配和列表调整大小花费更多时间用于更大的列表,时间差异可能更加明显。但情况并非总是如此(StackOverflow上有很好的文档记录str.join使用列表比使用CPython中的生成器更快,因为当str.join获取生成器时,它无论如何构造列表......)

1 您可以在将生成器表达式作为唯一参数传递给函数时省略括号 - 这种情况比您预期的更频繁......

答案 1 :(得分:1)

发电机懒洋洋地加载;你必须打个电话才能在每次需要时获得下一个值。

sum是一个聚合函数,它对整个可迭代的操作。您必须拥有可用于其工作的所有值。

列表理解工作得更快的原因是只有一个显式调用来获取整个列表,而一个显式操作将它们全部加起来。然而,使用生成器,你必须获得它的所有项目来执行它的聚合,并且由于它们有一百万,这导致一百万次调用。

这是渴望表现更好的案例之一。

答案 2 :(得分:0)

发电机版本获胜。 cProfile分析简单地介绍了genexp比列表理解更多的开销,因为它有更多的分析器对接的点。