In the previous lesson, we looked at for
loops and the term iteration. We can apply this word to any loop in other languages, but this word has another meaning in Python. Iteration is also an interaction of some object that supports the iteration protocol.
First, let us break down what a protocol is in the context of Python. A protocol is a set of specific actions on an object.
If object A allows you to perform actions on it defined by protocol B, then we say:
- Object A implements protocol B
- Or object A supports protocol B
In the courses that follow, you will learn that there are many different protocols in Python.
Even many of the language's syntactic constructs work for many different objects similarly precisely because they implement specific protocols.
This way, we can substitute strings and values of other types in the template because these types implement the string-conversion protocol. In Python, we find protocols at every turn.
What is iteration
Iteration is one of the most essential protocols in Python. After all, it allows the for
loop to work with collections consistently.
What is this protocol all about? The protocol requires the object to be iterable — that is, to have the special __iter__
method.
If you call the __iter__
method on an iterated object, the method should return a new specific object, the so-called iterator. The iterator must have the __next__
method.
Let us look at a proper example: iterating through a list. Lists are iterable, so this is perfect.
So, let us create a list and an iterator for it:
l = [1, 2, 3, 5, 8, 11]
i = iter(l)
print(i) # => <list_iterator object at 0x7f517843a240>
We have called the iter
function for the list, but this function calls the __iter__
method on the list.
It is done for code reading convenience because it is not great to read names like __foo__
. Other functions do something similar, such as the len
function.
We call most special methods with similar names inside language constructs.
Now we have an iterator i
. Let us try to call the __next__
method on it, both directly and with the more convenient next
function:
i.__next__() # 1
i.__next__() # 2
next(i) # 3
next(i) # 5
As we can see, each time we call the method, it returns another item from the original list. It also remembers the position in the list between calls. In this way, the iterator acts as a cursor in your text editor: if you press the arrow keys, the cursor moves and points to a new location in the text. The only difference is that the iterator is a cursor that can only move in one direction.
But what happens when the list runs out of items? Let us check it out:
next(i) # 8
next(i) # 11
next(i)
# Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# StopIteration