Is there a reason to prefer using map()
over list comprehension or vice versa? Is either of them generally more efficient or considered generally more pythonic
Actually, map
and list comprehensions behave quite differently in the Python 3 language. Take a look at the following Python 3 program:
def square(x):
return x*x
squares = map(square, [1, 2, 3])
print(list(squares))
print(list(squares))
You might expect it to print the line "[1, 4, 9]" twice, but instead it prints "[1, 4, 9]" followed by "[]". The first time you look at squares
it seems to behave as a sequence of three elements, but the second time as an empty one.
In the Python 2 language map
returns a plain old list, just like list comprehensions do in both languages. The crux is that the return value of map
in Python 3 (and imap
in Python 2) is not a list - it's an iterator!
The elements are consumed when you iterate over an iterator unlike when you iterate over a list. This is why squares
looks empty in the last print(list(squares))
line.
To summarize:
Here is one possible case:
map(lambda op1,op2: op1*op2, list1, list2)
versus:
[op1*op2 for op1,op2 in zip(list1,list2)]
I am guessing the zip() is an unfortunate and unnecessary overhead you need to indulge in if you insist on using list comprehensions instead of the map. Would be great if someone clarifies this whether affirmatively or negatively.
I tried the code by @alex-martelli but found some discrepancies
python -mtimeit -s "xs=range(123456)" "map(hex, xs)"
1000000 loops, best of 5: 218 nsec per loop
python -mtimeit -s "xs=range(123456)" "[hex(x) for x in xs]"
10 loops, best of 5: 19.4 msec per loop
map takes the same amount of time even for very large ranges while using list comprehension takes a lot of time as is evident from my code. So apart from being considered "unpythonic", I have not faced any performance issues relating to usage of map.
map
and filter
instead of list comprehensions.An objective reason why you should prefer them even though they're not "Pythonic" is this:
They require functions/lambdas as arguments, which introduce a new scope.
I've gotten bitten by this more than once:
for x, y in somePoints:
# (several lines of code here)
squared = [x ** 2 for x in numbers]
# Oops, x was silently overwritten!
but if instead I had said:
for x, y in somePoints:
# (several lines of code here)
squared = map(lambda x: x ** 2, numbers)
then everything would've been fine.
You could say I was being silly for using the same variable name in the same scope.
I wasn't. The code was fine originally -- the two x
s weren't in the same scope.
It was only after I moved the inner block to a different section of the code that the problem came up (read: problem during maintenance, not development), and I didn't expect it.
Yes, if you never make this mistake then list comprehensions are more elegant.
But from personal experience (and from seeing others make the same mistake) I've seen it happen enough times that I think it's not worth the pain you have to go through when these bugs creep into your code.
Use map
and filter
. They prevent subtle hard-to-diagnose scope-related bugs.
Don't forget to consider using imap
and ifilter
(in itertools
) if they are appropriate for your situation!
If you plan on writing any asynchronous, parallel, or distributed code, you will probably prefer map
over a list comprehension -- as most asynchronous, parallel, or distributed packages provide a map
function to overload python's map
. Then by passing the appropriate map
function to the rest of your code, you may not have to modify your original serial code to have it run in parallel (etc).