348

The Python 2 documentation says:

Built-in Functions: map(function, iterable, ...)

Apply function to every item of iterable and return a list of the results. If additional iterable arguments are passed, function must take that many arguments and is applied to the items from all iterables in parallel.

If one iterable is shorter than another it is assumed to be extended with None items.

If function is None, the identity function is assumed; if there are multiple arguments, map() returns a list consisting of tuples containing the corresponding items from all iterables (a kind of transpose operation).

The iterable arguments may be a sequence or any iterable object; the result is always a list.

What role does this play in making a Cartesian product?

content = map(tuple, array)

What effect does putting a tuple anywhere in there have? I also noticed that without the map function the output is abc and with it, it's a, b, c.

I want to fully understand this function. The reference definitions is also hard to understand. Too much fancy fluff.

MisterMiyagi
  • 44,374
  • 10
  • 104
  • 119
Web Master
  • 4,240
  • 6
  • 20
  • 28
  • 2
    What do you actually want to achieve and why specifically do you want to use `map`? – Kris Harper Jun 11 '12 at 01:42
  • so map just basically applies something to every element? – Web Master Jun 11 '12 at 02:07
  • 3
    @WebMaster yes, per the first sentence in the documentation that you pasted - "Apply function to every item of iterable". The rest of the paragraph is about more complex cases - like `map(None, a, b, c)` turns out to do `zip(a, b, c)`. But you very rarely see that in practice, precisely because the `zip` call is equivalent. – lvc Jun 11 '12 at 02:11
  • wait so in this case. map(tuple, array) what does tuple do? – Web Master Jun 11 '12 at 02:20
  • 2
    `tuple` is a function (well, its more nuanced than that, but it behaves like a function) that takes an iterable, and gives you a tuple with the same elements - so `tuple([1, 2, 3])` is equivalent to `(1, 2, 3)`. For `map(tuple, array)`, `array` would be an iterable of iterables (think a list of lists), and it gives you back each inner list turned into a tuple. – lvc Jun 11 '12 at 02:26
  • @lvc okay thank you. one last request to make things clear as possible, can someone give me an example of using map() + tuple to convert a string into an array? – Web Master Jun 11 '12 at 02:30
  • 1
    In general, it is the first sentence of the documentation of any function that matters the most. If you understand that, you get the gist of it. The rest of it specifies the behaviour in great detail, and some of that *will* be a bit opaque to start with, and you may need to come across an odd idiom based on it before you see "oh, *that's* what that means!". But once you get that lightbulb moment for a few builtins, you should start being able to understand the docs a bit more easily. – lvc Jun 11 '12 at 02:32
  • @lvc is there a way to find like the original code of the functions? – Web Master Jun 11 '12 at 02:35
  • 1
    @WebMaster see my updated answer for an example of that. The original code of functions is available on python.org, but a lot of it won't help you much - it is often written in C using the Python API, and very highly optimised making it harder to understand. We say things like "is equivalent to this somewhat easy to understand Python code" rather than "is implemented thus" for a reason. – lvc Jun 11 '12 at 02:42
  • Unfortunately, I did not find a single person on this page who correctly understands the function map() – Alexander May 13 '22 at 23:39
  • "What effect does putting a tuple anywhere in there have? I also noticed that without the map function the output is abc and with it, it's a, b, c." **What is this referring to**? Output **of what**? What is `array` before this line of code, and where does that value come from? Where was the code copied from, and *why should we expect* it to have *anything to do with* a cartesian product? – Karl Knechtel Aug 05 '22 at 10:26

6 Answers6

502

map isn't particularly pythonic. I would recommend using list comprehensions instead:

map(f, iterable)

is basically equivalent to:

[f(x) for x in iterable]

map on its own can't do a Cartesian product, because the length of its output list is always the same as its input list. You can trivially do a Cartesian product with a list comprehension though:

[(a, b) for a in iterable_a for b in iterable_b]

The syntax is a little confusing -- that's basically equivalent to:

result = []
for a in iterable_a:
    for b in iterable_b:
        result.append((a, b))
myildirim
  • 2,248
  • 2
  • 19
  • 25
dave
  • 12,634
  • 3
  • 20
  • 12
  • 50
    I find using `map` much less verbose than list comprehensions, at least for the case you are demonstrating. – marbel Dec 14 '16 at 16:40
  • 1
    How do I use map for properties? What is the `map`-equivalent of `[v.__name__ for v in (object, str)]`? – A Sz Jul 19 '17 at 10:53
  • @ASz How about `map(lambda v: v.__name__, list)`? – Kilian Oct 13 '17 at 07:55
  • 14
    map is faster since it doesn't call functions based on length of iterators .. calling functions has overhead .. Watch 6:00 https://www.youtube.com/watch?v=SiXyyOA6RZg&t=813s – anati Nov 22 '17 at 10:47
  • 1
    @anati I thought `map` was *sometimes* faster than comprehensions, sometimes not, precisely because of function call overhead? In particular, the heuristic I learned is that when using `map` requires you to introduce an extra function call, comprehensions are faster? E.g. I was led to believe that `map(lambda foo: foo.bar, my_list)` is slower than `foo.bar for foo in my_list`, and that even `map(operator.add, my_list_of_pairs)` is slower than `x + y for x, y in my_list_of_pairs`, precisely because of the additional function call. – mtraceur Feb 20 '18 at 22:21
  • @dave is `.map()` equivalent? `def split_input_target(chunk): input_text = chunk[:-1] target_text = chunk[1:] return input_text, target_text dataset = sequences.map(split_input_target)` seen here: https://colab.research.google.com/github/tensorflow/docs/blob/master/site/en/r2/tutorials/text/text_generation.ipynb#scrollTo=9NGu-FkO_kYU – mLstudent33 May 06 '19 at 05:19
  • Cartesian product argument is invalid nowadays because of `itertools.product`, e.g. `itertools.product('abc', 'defg')` will produce all pairs of these letters. – Drey Aug 08 '19 at 18:04
  • And tested, for some examples, `itertools.product` is few hundred times faster then list comprehension. – Drey Aug 08 '19 at 18:15
  • Thank you for using the right term "list comprehension". You can not imagine how difficult it was to come up with the right phrase to ask google. Fortunately "map shorthand" got me close enough – Peter Chaula Apr 09 '21 at 18:27
96

map doesn't relate to a Cartesian product at all, although I imagine someone well versed in functional programming could come up with some impossible to understand way of generating a one using map.

map in Python 3 is equivalent to this:

def map(func, iterable):
    for i in iterable:
        yield func(i)

and the only difference in Python 2 is that it will build up a full list of results to return all at once instead of yielding.

Although Python convention usually prefers list comprehensions (or generator expressions) to achieve the same result as a call to map, particularly if you're using a lambda expression as the first argument:

[func(i) for i in iterable]

As an example of what you asked for in the comments on the question - "turn a string into an array", by 'array' you probably want either a tuple or a list (both of them behave a little like arrays from other languages) -

 >>> a = "hello, world"
 >>> list(a)
['h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd']
>>> tuple(a)
('h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd')

A use of map here would be if you start with a list of strings instead of a single string - map can listify all of them individually:

>>> a = ["foo", "bar", "baz"]
>>> list(map(list, a))
[['f', 'o', 'o'], ['b', 'a', 'r'], ['b', 'a', 'z']]

Note that map(list, a) is equivalent in Python 2, but in Python 3 you need the list call if you want to do anything other than feed it into a for loop (or a processing function such as sum that only needs an iterable, and not a sequence). But also note again that a list comprehension is usually preferred:

>>> [list(b) for b in a]
[['f', 'o', 'o'], ['b', 'a', 'r'], ['b', 'a', 'z']]
lvc
  • 34,233
  • 10
  • 73
  • 98
  • map (fun x -> (x,x)) doesn't seem hard to understand... (though getting a true cartesian product out of map would be impossible, anything map produces is always some form of a list) – Kristopher Micinski Jun 11 '12 at 02:04
40

map creates a new list by applying a function to every element of the source:

xs = [1, 2, 3]

# all of those are equivalent — the output is [2, 4, 6]
# 1. map
ys = map(lambda x: x * 2, xs)
# 2. list comprehension
ys = [x * 2 for x in xs]
# 3. explicit loop
ys = []
for x in xs:
    ys.append(x * 2)

n-ary map is equivalent to zipping input iterables together and then applying the transformation function on every element of that intermediate zipped list. It's not a Cartesian product:

xs = [1, 2, 3]
ys = [2, 4, 6]

def f(x, y):
    return (x * 2, y // 2)

# output: [(2, 1), (4, 2), (6, 3)]
# 1. map
zs = map(f, xs, ys)
# 2. list comp
zs = [f(x, y) for x, y in zip(xs, ys)]
# 3. explicit loop
zs = []
for x, y in zip(xs, ys):
    zs.append(f(x, y))

I've used zip here, but map behaviour actually differs slightly when iterables aren't the same size — as noted in its documentation, it extends iterables to contain None.

Cat Plus Plus
  • 125,936
  • 27
  • 200
  • 224
20

Simplifying a bit, you can imagine map() doing something like this:

def mymap(func, lst):
    result = []
    for e in lst:
        result.append(func(e))
    return result

As you can see, it takes a function and a list, and returns a new list with the result of applying the function to each of the elements in the input list. I said "simplifying a bit" because in reality map() can process more than one iterable:

If additional iterable arguments are passed, function must take that many arguments and is applied to the items from all iterables in parallel. If one iterable is shorter than another it is assumed to be extended with None items.

For the second part in the question: What role does this play in making a Cartesian product? well, map() could be used for generating the cartesian product of a list like this:

lst = [1, 2, 3, 4, 5]

from operator import add
reduce(add, map(lambda i: map(lambda j: (i, j), lst), lst))

... But to tell the truth, using product() is a much simpler and natural way to solve the problem:

from itertools import product
list(product(lst, lst))

Either way, the result is the cartesian product of lst as defined above:

[(1, 1), (1, 2), (1, 3), (1, 4), (1, 5),
 (2, 1), (2, 2), (2, 3), (2, 4), (2, 5),
 (3, 1), (3, 2), (3, 3), (3, 4), (3, 5),
 (4, 1), (4, 2), (4, 3), (4, 4), (4, 5),
 (5, 1), (5, 2), (5, 3), (5, 4), (5, 5)]
Óscar López
  • 232,561
  • 37
  • 312
  • 386
20

The map() function is there to apply the same procedure to every item in an iterable data structure, like lists, generators, strings, and other stuff.

Let's look at an example: map() can iterate over every item in a list and apply a function to each item, than it will return (give you back) the new list.

Imagine you have a function that takes a number, adds 1 to that number and returns it:

def add_one(num):
  new_num = num + 1
  return new_num

You also have a list of numbers:

my_list = [1, 3, 6, 7, 8, 10]

if you want to increment every number in the list, you can do the following:

>>> map(add_one, my_list)
[2, 4, 7, 8, 9, 11]

Note: At minimum map() needs two arguments. First a function name and second something like a list.

Let's see some other cool things map() can do. map() can take multiple iterables (lists, strings, etc.) and pass an element from each iterable to a function as an argument.

We have three lists:

list_one = [1, 2, 3, 4, 5]
list_two = [11, 12, 13, 14, 15]
list_three = [21, 22, 23, 24, 25]

map() can make you a new list that holds the addition of elements at a specific index.

Now remember map(), needs a function. This time we'll use the builtin sum() function. Running map() gives the following result:

>>> map(sum, list_one, list_two, list_three)
[33, 36, 39, 42, 45]

REMEMBER:
In Python 2 map(), will iterate (go through the elements of the lists) according to the longest list, and pass None to the function for the shorter lists, so your function should look for None and handle them, otherwise you will get errors. In Python 3 map() will stop after finishing with the shortest list. Also, in Python 3, map() returns an iterator, not a list.

wec
  • 237
  • 3
  • 14
BlooB
  • 955
  • 10
  • 23
10

Python3 - map(func, iterable)

One thing that wasn't mentioned completely (although @BlooB kinda mentioned it) is that map returns a map object NOT a list. This is a big difference when it comes to time performance on initialization and iteration. Consider these two tests.

import time
def test1(iterable):
    a = time.clock()
    map(str, iterable)
    a = time.clock() - a

    b = time.clock()
    [ str(x) for x in iterable ]
    b = time.clock() - b

    print(a,b)


def test2(iterable):
    a = time.clock()
    [ x for x in map(str, iterable)]
    a = time.clock() - a

    b = time.clock()
    [ str(x) for x in iterable ]
    b = time.clock() - b

    print(a,b)


test1(range(2000000))  # Prints ~1.7e-5s   ~8s
test2(range(2000000))  # Prints ~9s        ~8s

As you can see initializing the map function takes almost no time at all. However iterating through the map object takes longer than simply iterating through the iterable. This means that the function passed to map() is not applied to each element until the element is reached in the iteration. If you want a list use list comprehension. If you plan to iterate through in a for loop and will break at some point, then use map.

Community
  • 1
  • 1
Ranga
  • 620
  • 1
  • 7
  • 9