Python中的Generators vs List Comprehension性能

cll*_*ach 6 python profiling list-comprehension generator

目前我正在学习生成器和列表理解,并且弄乱了剖析器以查看性能增益,并且在这两个中使用两者中的大量素数的总和数量的混乱.

我可以在生成器中看到:1 genexpr作为累积时间方式比列表对应方式短,但第二行是令我感到困惑的.正在做一个我认为是数字检查的电话是素数,但是不应该是另一个:列表理解中的1个模块?

我在个人资料中遗漏了什么吗?

In [8]: cProfile.run('sum((number for number in xrange(9999999) if number % 2 == 0))')
         5000004 function calls in 1.111 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
  5000001    0.760    0.000    0.760    0.000 <string>:1(<genexpr>)
        1    0.000    0.000    1.111    1.111 <string>:1(<module>)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        1    0.351    0.351    1.111    1.111 {sum}



In [9]: cProfile.run('sum([number for number in xrange(9999999) if number % 2 == 0])')
         3 function calls in 1.123 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    1.075    1.075    1.123    1.123 <string>:1(<module>)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        1    0.048    0.048    0.048    0.048 {sum}
Run Code Online (Sandbox Code Playgroud)

Ash*_*ary 5

首先调用的是next(或__next__在Python 3中)生成器对象的方法,而不是用于某些偶数检查.

在Python 2中,你不会为列表理解(LC)获得任何额外的行,因为LC没有创建任何对象,但是在Python 3中你将因为现在使它类似于生成器表达式而附加的代码对象(<listcomp>)是也为LC创建.

>>> cProfile.run('sum([number for number in range(9999999) if number % 2 == 0])')
         5 function calls in 1.751 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    1.601    1.601    1.601    1.601 <string>:1(<listcomp>)
        1    0.068    0.068    1.751    1.751 <string>:1(<module>)
        1    0.000    0.000    1.751    1.751 {built-in method exec}
        1    0.082    0.082    0.082    0.082 {built-in method sum}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

>>> cProfile.run('sum((number for number in range(9999999) if number % 2 == 0))')
         5000005 function calls in 2.388 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
  5000001    1.873    0.000    1.873    0.000 <string>:1(<genexpr>)
        1    0.000    0.000    2.388    2.388 <string>:1(<module>)
        1    0.000    0.000    2.388    2.388 {built-in method exec}
        1    0.515    0.515    2.388    2.388 {built-in method sum}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
Run Code Online (Sandbox Code Playgroud)

虽然1(LC)与生成器表达式中的5000001相比调用次数不同,但这大部分是因为sum消耗迭代器因此必须调用其__next__方法500000 + 1次(最后1可能是StopIteration为了结束迭代).对于列表理解,所有的魔法都发生在它的代码对象中,LIST_APPEND它帮助它逐个将项目附加到列表中,即没有可见的调用cProfile.