2

Python's map can take multiple iterables, for use when the callable can accept the same number of input arguments. If the input iterables are the same length, thats behaving like the list comprehension passing in zipped arguments, e.g.:

>>> iterables = 'spam', 'eggs'
>>> map(max, *iterables)
['s', 'p', 'g', 's']
>>> [max(*a) for a in zip(*iterables)]
['s', 'p', 'g', 's']

When the input arguments are different length, it gets weird - Python 2 (docs) pads with None, but Python 3 (docs) truncates to shortest iterable.

>>> map(max, 'spam', 'potato')  # 2.x
['s', 'p', 't', 'm', 't', 'o']
>>> list(map(max, 'spam', 'potato'))  # 3.x
['s', 'p', 't', 'm']

Why is this feature existing, what's an example of a typical case where that's needed or useful? I don't know a lot about functional styles, could I be missing out on some great strengths of map that are related to the multiple arguments? And what's the rationale for the API change in 3.x?

wim
  • 266,989
  • 79
  • 484
  • 630
  • 1
    Guido actually has a strong dislike of `map` and did in fact plan to remove it from python 3. Due to popular opinion however, he did not. http://www.artima.com/weblogs/viewpost.jsp?thread=98196 – Slater Victoroff Dec 17 '13 at 23:52
  • It's Python's fault that `lambda`s are too restrictive and too slow. Otherwise `map` would be useful in some cases where the list comprehension would turn harder to read. – Mihai Maruseac Dec 17 '13 at 23:55
  • 3
    `map` predates comprehensions. – user2357112 supports Monica Dec 18 '13 at 00:00
  • 1
    @MihaiMaruseac `lambda`s are **useless**. If you want to use more than one expression just use a `def` and define a real function. Having bigger `lambda`s would be completely unreadable. Other languages need `lambda` expressions because they cannot define inner functions, but python *does* have them, so `lambda`s are useful *only* when you need a thorw-away one-expr function to put as parameter. – Bakuriu Dec 18 '13 at 08:08

1 Answers1

4

Regarding why map truncates in python3, this is simply because python3's map is actually itertools.imap. And the documentation says:

Like map() but stops when the shortest iterable is exhausted instead of filling in None for shorter iterables. The reason for the difference is that infinite iterator arguments are typically an error for map() (because the output is fully evaluated) but represent a common and useful way of supplying arguments to imap().

The truncation allows you to do things like map(func, itertools.repeat(5), [1,2,3]) and iterate over the result without worries. With the old map that would be an infinite loop.

One of the most significant changes in python3 is that a lot of built-in functions now return generators instead of lists, including map and zip. This "increased lazyness" changed the way these functions should be used and thus the modified behaviour.

As to why one would ever use python2's multiple-iterables to map I don't know. Sure, it's a shortcut for something like (in python3):

list(itertools.starmap(function, itertools.zip_longest(*iterables)))

This might have some corner case usage, but I've never seen it used. Probably most people don't even know that map accepts a sequence of iterables. So, AFAIK there isn't any super-power deriving from the use of multiple arguments.

As to why map is in the language, that's because map was there long before list-comprehensions. Before list-comprehensions it was quite useful for building lists. It wasn't remove for backward compatibility and because many people actually like it, although Guido did want to remove it.

To learn more about the history of map, filter and reduce and other functional aspects, read: The History of Python: Origins of Python's "Functional" Features

Bakuriu
  • 85,459
  • 18
  • 168
  • 202
  • Wow, I guess I'm a relative newbie to python because I can't even imagine the language without comprehensions. They seem such a strong and integral part of the language that python < v2.0 must look like something quite different. – wim Dec 18 '13 at 10:50
  • 2
    @wim Yes, they are one of the "killer features" of newer python versions. Python <2.0 was really a *completely* different language. Especially python < 1.6 where strings didn't have methods (that's why in python2 the `string` module is full of functions like `split`, `strip` etc.). The language has *improved* a lot in the last 10-15 years. – Bakuriu Dec 18 '13 at 12:23