27

I'm new to Python. I'm using Python 3.3.2 and I'm having a hard time figuring out why the following code gives me an error:

strList = ['1','2','3']
intList = map(int,strList)
largest = max(intList)
smallest = min(intList)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: min() arg is an empty sequence

However this code gives me no errors at all:

strList = ['1','2','3']
intList = list(map(int,strList))
largest = max(intList)
smallest = min(intList)

My thought is that when intList is assigned to the return value of the map function, it becomes an iterator rather than a list, as per the docs. And perhaps as a side effect of calling max(), the iterator has been iterated to the end of the list, causing Python to believe the list is empty. (I'm drawing from C knowledge here. I'm not familiar with how iterators truly work in Python.) The only evidence I have to support this is that, for the first block of code:

>>> type(intList)
<class 'map'>

whereas for the second block of code:

>>> type(intList)
<class 'list'>

Can someone confirm or deny this for me please?

wjandrea
  • 28,235
  • 9
  • 60
  • 81
Dizzyspiral
  • 745
  • 6
  • 12

2 Answers2

34

You are exactly correct. In Python 3, map returns an iterator, which you can only iterate over once. If you iterate over an iterator a second time, it will raise StopIteration immediately, as though it were empty. max consumes the whole thing, and min sees the iterator as empty. If you need to use the elements more than once, you need to call list to get a list instead of an iterator.

user2357112
  • 260,549
  • 28
  • 431
  • 505
  • you can also call tuple or set on map object if it suits your purposes better than list – fanny Jan 25 '18 at 01:12
  • So Python 3 breaks Python 2 software? No backwards compatibility? I have the same error trying to reuse a `gtk.TreeIter`. – Luis A. Florit May 30 '20 at 16:40
  • 2
    @LuisA.Florit: Yes, that was the idea behind Python 3. It was the version where they broke backward compatibility to change the things they couldn't change without breaking backward compatibility. – user2357112 May 30 '20 at 21:34
  • @LuisA.Florit: I don't think what you're trying to do would have worked on Python 2 either - TreeIter sounds like it would have still been an iterator on Python 2 - but the `gtk.TreeIter` docs are bizarre, so I'm not sure how you're supposed to use a TreeIter in any Python version. – user2357112 May 30 '20 at 21:37
  • @user2357112supportsMonica: It worked before. I even had a list of iters that used everywhere in my program. It broke badly, I had to switch to and indexed `list` in the "classical" way. – Luis A. Florit May 30 '20 at 23:32
8

from your map documentation:

Return an iterator that applies function to every item of iterable, yielding the results.

and from http://docs.python.org/3/library/stdtypes.html#typeiter

Once an iterator’s next() method raises StopIteration, it must continue to do so on subsequent calls.

So an iterator, regardless of the underlying data object, can only be used once. It builds on the concept of a generator.

itertools.tee can be used make multiple independent iterators from one.

l1,l2 = itertools.tee(intList,2)
max(l1)
min(l2)
hpaulj
  • 221,503
  • 14
  • 230
  • 353