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()
andfilter()
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