Home > Mobile >  Comparison of Generator Expression with other from 'Learning Python.. by Mark Lutz'
Comparison of Generator Expression with other from 'Learning Python.. by Mark Lutz'

Time:10-05

While reading 'Learning Python by Mark Lutz', I came across a chapter on Generator function and Expression. Everything was fine but I am stuck over this line:

"For example, generator expressions often are equivalent to 3.X map calls...."

list(map(abs,(-1,-2,3,4))
list(abs(x) for x in (-1,-2,3,4))

I know what is going on in above two lines. But I don't get how to decide which method is better? Below is another example:

list(map(lambda x: x*2, (1,2,3,4))
list(x*2 for x in (1,2,3,4))

Which one is better in this case?

The text futher goes like this "..Like List comprehension, though, generator expressions may be similar to code when the operation applied is not a function call.."

Here, can some one please explain me the meaning of this line?

CodePudding user response:

Which one is better in this case?

Historically list comprehension were introduced as more concise alternative to using map or filter, PEP 202 - List Comprehensions Rationale for this feature is

List comprehensions provide a more concise way to create lists in situations where map() and filter() and/or nested loops would currently be used.

Disclaimer: map() and filter() must be here understood in Python-2.x sense NOT Python-3.x as this document is dated Jul 2000.

CodePudding user response:

But I don't get how to decide which method is better?

Better is, of course, somewhat subjective. Some people prefer the compactness of map(). Others prefer the explicitness of a generator expression because the for jumps out and makes it clear there's an iteration.

Performance-wise, map() is usually faster, because most of the work happens inside the interpreter (in the case of CPython, this means compiled C code) and doesn't require executing much interpreted bytecode. Note: this mostly applies to your first pair of examples. Using a lambda obviously means running interpreted code.

As a quick example of performance:

% python3 -m timeit "list(map(abs, (-1, -2, 3, 4)))"
500000 loops, best of 5: 587 nsec per loop

% python3 -m timeit "list(abs(x) for x in (-1, -2, 3, 4))"
500000 loops, best of 5: 870 nsec per loop

The generator expression takes 50% longer than map().

You can see in the disassembly of the statements that map() requires less interpreted code:

>>> dis.dis("list(map(abs, (-1, -2, 3, 4)))")
  1           0 LOAD_NAME                0 (list)
              2 LOAD_NAME                1 (map)
              4 LOAD_NAME                2 (abs)
              6 LOAD_CONST               0 ((-1, -2, 3, 4))
              8 CALL_FUNCTION            2
             10 CALL_FUNCTION            1
             12 RETURN_VALUE

versus the generator expression:

>>> dis.dis("list(abs(x) for x in (-1, -2, 3, 4))")
  1           0 LOAD_NAME                0 (list)
              2 LOAD_CONST               0 (<code object <genexpr> at 0x6fffffe0df50, file "<dis>", line 1>)
              4 LOAD_CONST               1 ('<genexpr>')
              6 MAKE_FUNCTION            0
              8 LOAD_CONST               2 ((-1, -2, 3, 4))
             10 GET_ITER
             12 CALL_FUNCTION            1
             14 CALL_FUNCTION            1
             16 RETURN_VALUE

Disassembly of <code object <genexpr> at 0x6fffffe0df50, file "<dis>", line 1>:
  1           0 LOAD_FAST                0 (.0)
        >>    2 FOR_ITER                14 (to 18)
              4 STORE_FAST               1 (x)
              6 LOAD_GLOBAL              0 (abs)
              8 LOAD_FAST                1 (x)
             10 CALL_FUNCTION            1
             12 YIELD_VALUE
             14 POP_TOP
             16 JUMP_ABSOLUTE            2
        >>   18 LOAD_CONST               0 (None)
             20 RETURN_VALUE
  • Related