In Python, iterators are intended for one-time use. Once an iterator has raised StopIteration, it shouldn't return any more values. Yet if I define a custom iterator, it seems that I can still sum the values after they're exhausted!
Example code (Python 3.6.5, or replace __next__(self)
with next(self)
to see the same behaviour in Python 2.7.15):
class CustomIterator:
def __iter__(self):
self.n=0
return self
def __next__(self):
self.n += 1
if self.n > 3:
raise StopIteration
return self.n
i1 = iter([1,2,3])
i2 = iter(CustomIterator())
print('Sum of i1 is {}'.format(sum(i1))) # returns 6 as expected
print('Sum of i1 is {}'.format(sum(i1))) # returns 0 because i1 is now exhausted
try:
print(next(i1))
except StopIteration:
print("i1 has raised StopIteration") # this exception happens
print('Sum of i1 is {}'.format(sum(i1))) # 0 again
print('Sum of i2 is {}'.format(sum(i2))) # returns 6 as expected
print('Sum of i2 is {}'.format(sum(i2))) # returns 6 again!
try:
print(next(i2))
except StopIteration:
print("i2 has raised StopIteration") # still get an exception
print('Sum of i2 is {}'.format(sum(i2))) # and yet we get 6 again
Why do i1 and i2 behave differently? Is it some trick in how sum
is implemented? I've checked https://docs.python.org/3/library/functions.html#sum and it doesn't give me a lot to go on.
Related questions:
- Summing values in an Iterator
- Why can't I iterate twice over the same data?
- Python iterator is empty after performing some action on it
These describe the expected behaviour for built-in iterators, but don't explain why my custom iterator behaves differently.