0

I ran into unexpected behaviour when working with map and list iterators in python3. In this MWE I first generate a map of maps. Then, I want the first element of each map in one list, and the remaining parts in the original map:

# s will be be a map of maps
s=[[1,2,3],[4,5,6]]
s=map(lambda l: map(lambda t:t,l),s)

# uncomment to obtain desired output
# s = list(s) # s is now a list of maps

s1 = map(next,s)

print(list(s1))
print(list(map(list,s)))

Running the MWE as is in python 3.4.2 yields the expected output for s1:

s1 = ([1,4]),

but the empty list [] for s. Uncommenting the marked line yields the correct output, s1 as above, but with the expected output for s as well:

s=[[2,3],[5,6]].

The docs say that map expects an iterable. To this day, I saw no difference between map and list iterators. Could someone explain this behaviour?

PS: Curiously enough, if I uncomment the first print statement, the initial state of s is printed. So it could also be that this behaviour has something to do with a kind of lazy(?) evaluation of maps?

3
  • list() is not an iterator. Commented Sep 15, 2015 at 10:30
  • You exhausted the s map by iterating over it via s1. I'm not sure why you think list() has anything to do with this. Commented Sep 15, 2015 at 10:33
  • Yes, I guess that the bottom line is that an iterator is not to be confused with an iterable. Commented Sep 15, 2015 at 12:10

1 Answer 1

2

A map() is an iterator; you can only iterate over it once. You could get individual elements with next() for example, but once you run out of items you cannot get any more values.

I've given your objects a few easier-to-remember names:

>>> s = [[1, 2, 3], [4, 5, 6]]
>>> map_of_maps = map(lambda l: map(lambda t: t, l), s)
>>> first_elements = map(next, map_of_maps)

Iterating over first_elements here will in turn iterate over map_of_maps. You can only do so once, so once we run out of elements any further iteration will fail:

>>> next(first_elements)
1
>>> next(first_elements)
4
>>> next(first_elements)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

list() does exactly the same thing; it takes an iterable argument, and will iterate over that object to create a new list object from the results. But if you give it a map() that is already exhausted, there is nothing to copy into the new list anymore. As such, you get an empty result:

>>> list(first_elements)
[]

You need to recreate the map() from scratch:

>>> map_of_maps = map(lambda l: map(lambda t: t, l), s)
>>> first_elements = map(next, map_of_maps)
>>> list(first_elements)
[1, 4]
>>> list(first_elements)
[]

Note that a second list() call on the map() object resulted in an empty list object, once again.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks! I didn't consider that I exhausted the map iterator during the first operation. I guess my frequent use of haskell (where a map over a list returns a list) made me not see this. Could you comment on the execution order of the mappings, i.e. why map_of_maps is not exhausted when I do not evaluate (print) first_elements?
@SerJothanChanes: in Python 2, map() used to return a list too, but in Python 3 the built-in tools have been converted to iterators; more memory efficient that way.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.