enumerate()
returns an iterator, as do the other calls.. You can only loop through an iterator once; it is then exhausted.
You can create such an iterator yourself with a generator function:
def somelist_generator():
somelist = [1, 2, 3]
while somelist:
yield somelist.pop()
If you were to loop over somelist_generator()
, the list somelist
would be emptied. You can only do that once, since .pop()
removes elements:
>>> it = somelist_generator()
>>> for i in it:
... print(i)
...
3
2
1
>>> next(it)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
The next()
call tries to get another value from the it
iterator; it is already empty so the StopIteration
exception is raised. That exception signals there are no more elements to be gotten, and that's why you end up with an empty list the second time you try and get anything from an iterator:
>>> list(it)
[]
range()
does not return an iterator. It returns a range object instead, which represents a memory-efficient series of numbers; only the start, end and stride need to be stored and everything else can be derived from those 3 points.