Why Results of Map() and List Comprehension Are Different

Why results of map() and list comprehension are different?

They are different, because the value of i in both the generator expression and the list comp are evaluated lazily, i.e. when the anonymous functions are invoked in f.

By that time, i is bound to the last value if t, which is -1.

So basically, this is what the list comprehension does (likewise for the genexp):

x = []
i = 1 # 1. from t
x.append(lambda: i)
i = -1 # 2. from t
x.append(lambda: i)

Now the lambdas carry around a closure that references i, but i is bound to -1 in both cases, because that is the last value it was assigned to.

If you want to make sure that the lambda receives the current value of i, do

f(*[lambda u=i: u for i in t])

This way, you force the evaluation of i at the time the closure is created.

Edit: There is one difference between generator expressions and list comprehensions: the latter leak the loop variable into the surrounding scope.

List comprehension vs map

map may be microscopically faster in some cases (when you're not making a lambda for the purpose, but using the same function in map and a list comprehension). List comprehensions may be faster in other cases and most (not all) Pythonistas consider them more direct and clearer.

An example of the tiny speed advantage of map when using exactly the same function:

$ python -m timeit -s'xs=range(10)' 'map(hex, xs)'
100000 loops, best of 3: 4.86 usec per loop

$ python -m timeit -s'xs=range(10)' '[hex(x) for x in xs]'
100000 loops, best of 3: 5.58 usec per loop

An example of how performance comparison gets completely reversed when map needs a lambda:

$ python -m timeit -s'xs=range(10)' 'map(lambda x: x+2, xs)'
100000 loops, best of 3: 4.24 usec per loop

$ python -m timeit -s'xs=range(10)' '[x+2 for x in xs]'
100000 loops, best of 3: 2.32 usec per loop

Python: List Comprehensions vs. map

All your timing results can be explained by theses facts:

  1. CPython has a rather high function call overhead.

  2. map(f, it) is slightly faster than [f(x) for x in it].

The first version of your code does not define a function at all, so there is no function call overhead. The second version needs to define a function, ,so there is function call overhead in every iteration. The third version is completely equivalent to the second one – functions and lambda expressions are the same thing. And the last version is even slower according to fact 2.

Map vs List vs loop in python 3.7

First of all, to have a fare comparison you have to convert the result of the map function to list. map in Python 3.X returns an iterator object not a list. Second of all, in CPython implementation built in functions are actually wrappers around c functions which makes them faster than any Python code with same functionality, although when you use lambda inside a built-in function you're actually breaking the chain and this will make it approximately as fast as a Python code.

Another important point is that list comprehension is just a syntactic sugar around a regular loop and you can use it to avoid extra function calls like appending to lists, etc.

why not always use map if its faster than the rest (list comprehension, loop (various variants))?

Performance is not always the first priority when writing code. In a lot of case, we might not care about the µs lost in the list comprehension, if it helps reading and maintaining the code later.

I believe list comprehension are more easier to read and understand than map, despite the small performance lost.

EDIT, as ayhan described in comments, your example is biased since map returns a generator, so it does not generate anything until iterated over, while the list comprehension compute the whole list on declaration.

Map vs list comprehension in Python

You might want to take a look at the responses to this question:

Python List Comprehension Vs. Map

Also, here's a relevant essay from Guido, creator and BDFL of Python:

http://www.artima.com/weblogs/viewpost.jsp?thread=98196

Personally, I prefer list comprehensions and generator expressions because their meaning is more obvious when reading the code.

Equivalent of List Comprehension Using Map and Filter

Since both filter and map work over individual items in a sequence, you would have to view your logic from the perspective of each item in the list, rather than a combination of items, and that means you need to rephrase the expressions used in your list comprehension as functions of individual items. Instead of the filter condition x + y == tgt, therefore, it is beneficial to view it as x == tgt - y, where y also has to be an item in the num list, so that your list comprehension can be rewritten as:

[num.index(x) for x in num if x in {tgt - y for y in num}]

With this equivalent list comprehension, it then becomes clear that to implement the filter condition would need to make a set by mapping each item in num to its difference with tgt, which can be done with the tgt.__sub__ method, and test each item x in num if it is a member of the set, which can be done with the set's __contains__ method, and finally, map the filtered sequence to num.index to output the index of each matching item:

list(map(num.index, filter(set(map(tgt.__sub__, num)).__contains__, num)))

This returns:

[0, 1, 2, 4, 6, 7]

Performance of loops, list comprehensions and maps in Python 3

A few pointers:

  1. Wrap your code in functions for clarity.
  2. You are missing list creation and appends in code_for. This is the bulk of the cost of using an explicit for loop.
  3. You can then use timeit, or if you have Jupyter notebook, the magic %timeit command.

As below, map without lambda performs best, which makes sense since hex is a built-in. See Python List Comprehension Vs. Map for more details.

def code_for(n):
res = []
for i in range(n):
res.append(hex(i))
return res

def code_map(n):
return list(map(hex, range(n)))

def code_map_lambda(n):
return list(map(lambda x: hex(x), range(n)))

def code_list_comprehension(n):
return [hex(x) for x in range(n)]

%timeit code_for(10000) # 3.19 ms per loop
%timeit code_map(10000) # 1.69 ms per loop
%timeit code_map_lambda(10000) # 3.06 ms per loop
%timeit code_list_comprehension(10000) # 2.27 ms per loop


Related Topics



Leave a reply



Submit