Repeating some calculation or something else is
iteration
.
The
Iterator
knows about it's own state/position.
An Iterator has the
__next__
method, which is called by for-loops, lists, ... and everything
which consumes an iterable. Iterable does not mean, that this object is an Iterator.
class IterableIterator:
def __init__(self):
self.position = 0
self.data = list(range(20))
def __iter__(self):
"""
When an object has the __next__ method, then
it is an Iterable.
"""
print('__iter__ called')
return self
def __next__(self):
"""
If this method exists, then the object
itself is an Iterator.
"""
print('__next__ called')
if self.position < 20:
element = self.data[self.position]
self.position += 1
return element
else:
# have to raise an exception
# as control flow for consumers
raise StopIteration
class Iterator:
def __init__(self, start, stop, step=1):
self.start = start
self.stop = stop
self.step = step
self.value = start
def __next__(self):
"""
When an object has the __next__ method, then
it is an Iterator.
"""
print('__next__ called')
if self.value < self.stop:
element = self.value
self.value += self.step
return element
else:
raise StopIteration
class Iterable:
def __iter__(self):
"""
This method is called by iter() or other
built-ins. If there is an __iter__ method, then
the object is Iterable
"""
print('__iter__ called')
return Iterator(0, 10, 1)
Output:
In [3]: it1 = IterableIterator()
In [4]: iter(it1) == it1.__iter__()
__iter__ called
__iter__ called
Out[4]: True
In [5]: list(it1)
__iter__ called
__next__ called
__next__ called
__next__ called
__next__ called
__next__ called
__next__ called
__next__ called
__next__ called
__next__ called
__next__ called
__next__ called
__next__ called
__next__ called
__next__ called
__next__ called
__next__ called
__next__ called
__next__ called
__next__ called
__next__ called
__next__ called
Out[5]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
In [6]: list(it1)
__iter__ called
__next__ called
Out[6]: []
In [7]: # iterator has now been consumed. It's a finite iterator.
In [8]: iterator_object = Iterator()
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-8-0bd53d26c3d1> in <module>
----> 1 iterator_object = Iterator()
TypeError: __init__() missing 2 required positional arguments: 'start' and 'stop'
In [9]: iterator_object = Iterator(10, 13)
In [10]: next(iterator_object)
__next__ called
Out[10]: 10
In [11]: next(iterator_object)
__next__ called
Out[11]: 11
In [12]: next(iterator_object)
__next__ called
Out[12]: 12
In [13]: next(iterator_object)
__next__ called
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-13-c06eadb484b5> in <module>
----> 1 next(iterator_object)
<ipython-input-2-606497307f1e> in __next__(self)
16 return element
17 else:
---> 18 raise StopIteration
19
20 class Iterable:
StopIteration:
In [14]: iterable_but_not_iterator = Iterable()
In [15]: next(iterable_but_not_iterator)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-15-173518d9b919> in <module>
----> 1 next(iterable_but_not_iterator)
TypeError: 'Iterable' object is not an iterator
In [16]: iterator_object2 = iter(iterable_but_not_iterator)
__iter__ called
In [17]: iterator_object3 = iter(iterable_but_not_iterator)
__iter__ called
In [18]: next(iterator_object2)
__next__ called
Out[18]: 0
In [19]: next(iterator_object3)
__next__ called
Out[19]: 0
In [20]: next(iterator_object2)
__next__ called
Out[20]: 1
In [21]: next(iterator_object2)
__next__ called
Out[21]: 2
In [22]: next(iterator_object3)
__next__ called
Out[22]: 1
In [23]: # two independent iterators. This is because of the implementation
...: # the class Iterable returns for each iter() call a new Iterator
...: # The Iterator holds the state about the position
...: # The Iterable returns an Iterator
...: # Objects without a __iter__ and __next__ method are
...: # still iterable, if they have a __len__ and __getitem__ method
...: # Everywhere Iteration is used
In [24]: