All iterators are consumed; the reason you might not think so is that when you use an iterable with something like
for x in [1,2,3]:
the for loop is creating a new iterator for you behind the scenes. In fact, a list is not an iterator; iter([1,2,3]) returns something of type list_iterator, not the list itself.
Regarding the example you linked to in a comment, instead of
class PowTwo:
def __init__(self, max=0):
self.max = max
def __iter__(self):
self.n = 0
return self
def __next__(self):
if self.n <= self.max:
result = 2 ** self.n
self.n += 1
return result
else:
raise StopIteration
which has the side effect of modifying the iterator in the act of returning it, I would do something like
class PowTwoIterator:
def __init__(self, max=0):
self.max = max
self._restart()
def _restart(self):
self._n = 0
def __iter__(self):
return self
def __next__(self):
if self._n <= self.max:
result = 2 ** self._n
self._n += 1
return result
else:
raise StopIteration
Now, the only way you can modify the state of the object is to do so explicitly (and even that should not be done lightly, since both _n and _restart are marked as not being part of the public interface).
The change in the name reminds you that this is first and foremost an iterator, not an iterable that can provide independent iterators from.
yieldin adefstatement creates a generator, but you can also create a generator with a generator expression, e.g.(x for x in [1,2,3]).