10

Does map() iterate through the list like "for" would? Is there a value in using map vs for?

If so, right now my code looks like this:

for item in items:
    item.my_func()

If it makes sense, I would like to make it map(). Is that possible? What is an example like?

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
roder
  • 566
  • 6
  • 13

9 Answers9

24

You could use map instead of the for loop you've shown, but since you do not appear to use the result of item.my_func(), this is not recommended. map should be used if you want to apply a function without side-effects to all elements of a list. In all other situations, use an explicit for-loop.

Also, as of Python 3.0 map returns an iterator, so in that case map will not behave the same (unless you explicitly evaluate all elements returned by the iterator, e.g. by calling list on it).


Edit: kibibu asks in the comments for a clarification on why map's first argument should not be a function with side effects. I'll give answering that question a shot:

map is meant to be passed a function f in the mathematical sense. Under such circumstances it does not matter in which order f is applied to the elements of the second argument (as long as they are returned in their original order, of course). More importantly, under those circumstances map(g, map(f, l)) is semantically equivalent to map(lambda x: g(f(x)), l), regardless of the order in which f and g are applied to their respective inputs.

E.g., it doesn't matter whether map returns an iterator or a full list at once. However, if f and/or g cause side effects, then this equivalence is only guaranteed if the semantics of map(g, map(f, l)) are such that at any stage g is applied to the first n elements returned by map(f, l) before map(f, l) applies f to the *(n + 1)*​st element of l. (Meaning that map must perform the laziest possible iteration---which it does in Python 3, but not in Python 2!)

Going one step further: even if we assume the Python 3 implementation of map, the semantic equivalence may easily break down if the output of map(f, l) is e.g. passed through itertools.tee before being supplied to the outer map call.

The above discussion may seem of a theoretic nature, but as programs become more complex, they become more difficult to reason about and therefore harder to debug. Ensuring that some things are invariant alleviates that problem somewhat, and may in fact prevent a whole class of bugs.

Lastly, map reminds many people of its truly functional counterpart in various (purely) functional languages. Passing it a "function" with side effects will confuse those people. Therefore, seeing as the alternative (i.e., using an explicit loop) is not harder to implement than a call to map, it is highly recommended that one restricts use of map to those cases in which the function to be applied does not cause side effects.

user2357112
  • 260,549
  • 28
  • 431
  • 505
Stephan202
  • 59,965
  • 13
  • 127
  • 133
  • Why isn't it recommended? Is it a semantics thing, or some sort of distributed processing thing (where side-effects break parallelism)? – kibibu Apr 07 '10 at 00:49
  • 1
    @kibibu: I started answering your question, but ended up with way too much text for this comment field to contain. So I updated the answer :) How this helps! (As for your remark about distributed processing of the iterable: though that *could* be cited as an additional argument when talking about `map` in other languages, I do not think Python (currently) performs any optimizations of that kind.) – Stephan202 Apr 08 '10 at 18:45
5

You can write this using map like this:

map(cls.my_func, items)

replacing cls with the class of the items you are iterating over.

As mentioned by Stephan202, this is not recommended in this case.

As a rule, if you want to create a new list by applying some function to each item in the list, use map. This has the implied meaning that the function has no side effect, and thus you could (potentially) run the map in parallel.

If you don't want to create a new list, or if the function has side effects, use a for loop. This is the case in your example.

Kiv
  • 31,940
  • 6
  • 44
  • 59
2

There is a slight semantic difference, which is probably closed in python language spec. The map is explicitly parallelizable, while for only in special situations. Code can break out from for, but only escape with exception from map.

In my opinion map shouldn't also guarantee order of function application while for must. AFAIK no python implementation is currently able to do this auto-parallelization.

Pasi Savolainen
  • 2,460
  • 1
  • 22
  • 35
2

You can switch Your map to some cool threaded OR multiprocessing OR distributed computing framework if You need to. Disco is an example of distributed, resistant to failures erlang-and-python based framework. I configured it on 2 boxes of 8 cores and now my program runs 16 times faster, thanks to the Disco cluster, however I had to rewrite my program from list comprehensions and for loops to map/reduce.

It's the same deal to write a program using for loops and list comprehensions and map/reduce, but when You need it to run on a cluster, You can do it almost for free if You used map/reduce. If You didn't, well, You will have to rewrite.

Beware: as far as I know, python 2.x returns a list instead of an iterator from map. I've heard this can be bypassed by using iter.imap() (never used it though).

Paweł Polewicz
  • 3,711
  • 2
  • 20
  • 24
2

Use an explicit for-loop when you don't need a list of results back (eg. functions with side-effects).

Use a list comprehension when you do need a list of results back (eg. functions that return a value based directly on the input).

Use map() when you're trying to convince Lisp users that Python is worth using. ;)

Kylotan
  • 18,290
  • 7
  • 46
  • 74
1

The main advantage of map is when you want to get the result of some calculation on every element in a list. For example, this snippet doubles every value in a list:

map(lambda x: x * 2, [1,2,3,4])  #=> [2, 4, 6, 8]

It is important to note that map returns a new list with the results. It does not modify the original list in place.

To do the same thing with for, you would have to create an empty list and add an extra line to the for body to add the result of each calculation to the new list. The map version is more concise and functional.

Jesse Hallett
  • 1,857
  • 17
  • 26
  • 4
    List comprehensions are even more concise though, e.g. [x*2 for x in [1,2,3,4]) in your example. – Kiv May 17 '09 at 20:03
0

There's a number of good answers already. I want to emphasis another aspect of map(): code readability and transparency.

Particularly, I'll focus on this part of the question:

Is there a value in using map vs for?


For me, using map() instead of loops or comprehensions, where appropriate, has the main benefit of making the code more transparet.

(For simplicity, below I assume that we're operating on a list, and I pretend that map() returns a list. [1])

For someone reading or glancing over the code, a list transformed with map() gives the reader additional information, and does so very cheaply. This information comes in two categories:

  • enforced by the implementation
  • conveying intent

With map(), rather than a loop or comprehension,

  • it is enforced (we know for sure) that:
    • the produced list has the same number of elements as the input list
    • the input list is not mutated
  • the programmer conveyes their intent that:
    • the function that is applied has no side effects

Note that within the Python community, it is generally considered more pythonic to use comprehensions or for loops rather than map().

[1]: Return type of map() and lazy evaluation

map() takes an iterable, and it returns something iterable-like in the form of a generator.

An example of iterable types are list and tuples.

You can treat the returned value as an iterator, but the value will be evaluated only when needed. The latter is a consequence of map() actually returning a generator. This is called lazy evaluation.

We can illustrate the behaviour with a function prnt() that returns None and has the side effect of printing its argument to the console:

>>> def prnt(x): print(f'printing {x}'); return None
>>> a = prnt(1)
printing 1
>>> print(a)
None

Now,

# list comprehension:
>>> c = [prnt(x) for x in [1,2]]
printing 1
printing 2
>>> print(c)
[None, None]

# map:
>>> b = map(prnt, [1,2])
# no output - prints nothing!
>>> b  # what is it?
<map at 0x11241e290>
>>> l=list(b)  # converting to list forces evaluation
printing 1
printing 2
>>> l
[None, None]
Carl
  • 937
  • 10
  • 21
0

Map can sometimes be faster for built-in functions than manually coding a for loop. Try timing map(str, range(1000000)) vs. a similar for loop.

Jack Gray
  • 46
  • 3
-3
map(lambda item: item.my_func(), items)
Aditya Mukherji
  • 9,099
  • 5
  • 43
  • 49
  • 1
    This does work, but doesn't answer the question of why this is better than using a for loop (namely, it isn't). – Kiv May 17 '09 at 20:08