Search code examples
pythonlisttuplesenumerate

Why do list and tuple change the output of enumerate?


I was surprised at the following behavior:

>>> a=['a','b','c','d','e','f','g']
>>> en=enumerate(a)
>>> list(en)
[(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd'), (4, 'e'), (5, 'f'), (6, 'g')]
>>> list(en)
[]
>>> # en changed?   let's try that again!
>>> a=['a','b','c','d','e','f','g']
>>> en=enumerate(a)
>>> tuple(en)
((0, 'a'), (1, 'b'), (2, 'c'), (3, 'd'), (4, 'e'), (5, 'f'), (6, 'g'))
>>> tuple(en)
()
>>> # en changes again?

I was presuming that list() would just construct a list from the data in en, and would not change it in any way. The same with tuple(). But they both affect en in a "profound" way. Am I misunderstanding something about list() and tuple(), or am I misunderstanding enumerate()? Thanks.


Solution

  • There's nothing special about list and tuple, either. This will happen however you consume en, including via your own hand-written for loop:

    >>> a=['a','b','c','d','e','f','g']
    >>> en=enumerate(a)
    >>> en
    <enumerate object at 0x21172d0>
    >>> for index, elem in en:
    ...   print index, elem
    ... 
    0 a
    1 b
    2 c
    3 d
    4 e
    5 f
    6 g
    >>> for index, elem in en:
    ...   print index, elem
    ... 
    >>> 
    

    Note that en is an "enumerate object", not a list or tuple, and that printing it didn't attempt to print its contents. This is because enumerate doesn't actually produce a data structure containing all the same data as a plus the index values. Instead it returns a small object that remembers internally which container it was iterating over, where it was up to, and what count it had reached. That's all you need to produce the "next" value, and so it can be iterated over even though it's not a container as such.

    The reason for this is that people almost never store the result of enumerate, it's usually called to immediately iterate over it with a for loop. For that purpose, it would be wasteful of time and memory to go to the effort of building a copy of all the data and hold all the indexes in memory at once. Producing them as you go is enough.

    If you do need to store the resulting data from enumerate to use more than once (or to use somewhere else than where you generated it), then you will need that copy. The easiest way to get that is actually to do something like en = list(enumerate(a)). Then you can use en exactly as you were expecting.