When map
has different-length inputs, a fill value of None
is used for the missing inputs:
>>> x = [[1,2,3,4],[5,6]]
>>> map(lambda *x:x, *x)
[(1, 5), (2, 6), (3, None), (4, None)]
This is the same behavior as:
>>> import itertools
>>> list(itertools.izip_longest(*x))
[(1, 5), (2, 6), (3, None), (4, None)]
What's the reason map
provides this behavior, and not the following?
>>> map(lambda *x:x, *x)
[(1, 5), (2, 6), (3,), (4,)]
…and is there an easy way to get the latter behavior either with some flavor of zip
or map
?
I think this a design decision that the core devs opted for at the time they implemented map
. There's no universally defined behavior for map
when it is used with multiple iterables, quoting from Map (higher-order function):
Map with 2 or more lists encounters the issue of handling when the lists are of different lengths. Various languages differ on this; some raise an exception, some stop after the length of the shortest list and ignore extra items on the other lists; some continue on to the length of the longest list, and for the lists that have already ended, pass some placeholder value to the function indicating no value.
So, Python core devs opted for None
as placeholder for shorter iterables at the time map
was introduced to Python in 1993.
But in case of itertools.imap
it short-circuits with the shortest iterable because its design is heavily inspired from languages like Standard ML, Haskell and APL. In Standard ML and Haskell map
ends with shortest iterable(I am not sure about APL though).
Python 3 also removed the map(None, ...)
(or we should say itertools.imap
, Python 3's map
is actually almost itertools.imap
: Move map()
from itertools
to builtins
) construct because it was present in Python 2 only because at the time map
was added to Python there was no zip()
function in Python.
From Issue2186: map
and filter
shouldn't support None
as first argument (in Py3k only):
I concur with Guido that we never would have created
map(None, ...)
ifzip()
had existed. The primary use case is obsolete.
To get the result you want I would suggest using itertools.izip_longest
with a sentinel value(object()
) rather than default None
, None
will break things if the iterables itself contain None
:
from itertools import izip_longest
def solve(seq):
sentinel = object()
return [tuple(x for x in item if x is not sentinel) for item in
izip_longest(*seq, fillvalue=sentinel)]
print solve([[1,2,3,4],[5,6]])
# [(1, 5), (2, 6), (3,), (4,)]
Given that the first list is always longer and that there are only two lists, you would do something like this:
x = [1,2,3,4,5]
y = ['a','b']
zip(x,y) + [(i,) for i in x[len(y):]]
[(1, 'a'), (2, 'b'), (3,), (4,), (5,)]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With