Generators vs List Comprehension performance in Python

前端 未结 1 709

Currently I was learning about generators and list comprehension, and messing around with the profiler to see about performance gains stumbled into this cProfile of a sum of

1条回答
  •  -上瘾入骨i
    2020-12-18 04:30

    First of all the calls are to next(or __next__ in Python 3) method of the generator object not for some even number check.

    In Python 2 you are not going to get any additional line for a list comprehension(LC) because LC are not creating any object, but in Python 3 you will because now to make it similar to a generator expression an additional code object() is created for a LC as well.

    >>> 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 :1()
            1    0.068    0.068    1.751    1.751 :1()
            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 :1()
            1    0.000    0.000    2.388    2.388 :1()
            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}
    

    The number of calls are different though 1(LC) compared to 5000001 in generator expression, this is most because sum is consuming the iterator hence has to call its __next__ method 500000 + 1 times(last 1 is probably for StopIteration to end the iteration). For a list comprehension all the magic happens inside its code object where the LIST_APPEND helps it in appending items one by one to the list, i.e no visible calls for cProfile.

    0 讨论(0)
提交回复
热议问题