What is not working with my "map" usage?

Peter Otten __peter__ at web.de
Sat Sep 22 15:11:00 EDT 2018


Victor via Python-list wrote:

> On Saturday, September 22, 2018 at 6:22:32 AM UTC-7, Peter Otten wrote:
>> Victor via Python-list wrote:
>> 
>> > Let me use a different input args and display them below.  Basically, I
>> > am
>> > hoping to add up all elements of each nested list.  So at first it
>> > should
>> > start with [1,11,111] ==> 1+11+111 = 123.  But instead, it appears to
>> > take
>> > the 1st element from each nested list to add up [1,2,3] = 6.   How
>> > should
>> > it be corrected?  Thx.
>> 
>> I see three options. You can
>> 
>> (1) use a list comprehension
>> 
>> [add_all_elements(*sub) for sub in alist]
>> 
>> (2) replace map() with itertools.starmap()
>> 
>> list(itertools.starmap(add_all_elements, alist))
>> 
>> (3) change your function's signature from add_all_elements(*args) to
>> add_all_elements(args), either by modifying it directly or by wrapping it
>> into another function
>> 
>> list(map(lambda args: add_all_elements(*args), alist))
>> 
>> (3a) My personal preference would be to change the signature and then use
>> the list comprehension
>> 
>> def add_all_elements(args): ...
>> [add_all_elements(sub) for sub in alist]
> 
> Hi Peter,
> Thank you for your suggested solutions.  They all work.  But I just want
> to know what is wrong with my doing:
> 
> list(map(add_all_elements,*alist))
> 
> Theoretically, each list element is passed to add_all_elements.  And if my
> alist is [[1, 11, 111], [2, 22, 222], [3, 33, 333]], then the 1st list
> element  must be this [1,11,111] passed as args into add_all_elements.
> 
> In other words, the following should have happened:
> 
>>>> add_all_elements (*[1,11,111])

That's not what happens. Try it with a function that passes through its 
arguments unchanged:

>>> items = [[1, 11, 111], [2, 22, 222], [3, 33, 333]]
>>> list(map(lambda *args: args, *items))
[(1, 2, 3), (11, 22, 33), (111, 222, 333)]

The star before items is evaluated directly rather than magically passed on 
to the call of add_all_elements.

map(f, *items)

is equivalent to

map(f, items[0], items[1], items[2])

which calls f with

f(items[0][0], items[1][0], items[2][0])
f(items[0][1], items[1][1], items[2][1])
f(items[0][2], items[1][2], items[2][2])

If you think of your list of lists as a matrix that matrix is effectively 
transposed (x and y axis are swapped).

>>> list(map(lambda *args: sum(args), *items))
[6, 66, 666]

In principle you could transpose the matrix twice

>>> list(map(lambda *args: sum(args), *zip(*items)))
[123, 246, 369]

but that's not a very efficient approach.

Another gotcha to be aware of is that map() (and zip()) stop when the 
shortest argument ends:

>>> list(map(lambda *args: sum(args), [], *items))
[]
>>> list(map(lambda *args: sum(args), [42], *items))
[48]
>>> list(map(lambda *args: sum(args), [42, 42], *items))
[48, 108]
>>> list(map(lambda *args: sum(args), [42, 42, 42, 42, 42], *items))
[48, 108, 708]





More information about the Python-list mailing list