Skip to main content
 首页 » 编程设计

Python 列表理解与生成器

2025年05月04日28jillzhang

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

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 

对比

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

请您参考如下方法:

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

sum(xrange(9999999)) 

通常,如果我需要做一些需要在推导式和生成器表达式之间进行选择的操作,我会这样做:

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

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

对于这样的事情,生成器通常会更快,因为它们避免创建中间列表(以及与之相关的内存分配)。随着列表变大,时间差异可能更加明显,因为内存分配和列表大小调整需要更多时间来处理更大的列表。然而,情况并非总是如此(在 StackOverflow 上有详细记录,str.join 使用列表比使用 CPython 中的生成器更快,因为当 str.join 获取生成器时,无论如何它都会构建列表...)。

1在将生成器表达式作为唯一参数传递给函数时,您可以随时省略括号——这种情况发生的频率比您预期的要高...