This post aims to describe the basic mechanisms behind iterators and generators.
As in many programming languages, Python allows to iterate over a collection. The iteration mechanism is often useful when we need to scan a sequence, operation that is very common in programming.
In Python the iterator protocol involves two components: an iterable and an iterator.
The iterable is the container through which we want to iterate. It is the object that needs to be scanned to retrieve all the elements (or some of them). Some of well-known iterables are lists, tuples, dictionaries, ranges. In the iterator protocol the iterable exposes a iter method that returns an iterator object.
The iterator is the data structure that allows the scan through the container. It could seem a complication, but actually, with the separation of concerns, it lets the developer separate the concept of container, from the concept of iteration. The container object doesn't need to keep the state of an iteration and furthermore, on the same object, many iterations at the same time can take place, so keeping the iteration state in a different object is a must. The container is a collection of elements, while the iterator is kind of an handler of the container, it exposes the same elements (owned by the container) one-by-one with a specific order and . In the iterator protocol, the iterator exposes two methods: iter and next. While the first one return the object itself (it allows the usage of both container and iterator in for and in statements), the latter return the next item from the container. What does it make the iterator end the iteration? The StopIteration exception.
Below an example of iterator protocol implementation:
And its usage:
Generators are methods with yield statements. The yield statement has the power to suspend the function execution and store its state, so that it can be resumed. Behind the scenes, python returns the control to the function caller and save the function state; in this way, at the next execution, the function will start where it left, without letting the developer worrying about the state of the function. Generators ARE iterators, but not vice-versa.
Here an example of generator:
and its usage:
Like generators they are async function with a yield statement.
- Iterators are iterables.
- Iterators are objects that implement the
iterator protocolconsisting in implementing both
- Iterator iterations stop when
- Generators are methods with yield statements.
- Async generators are async methods with yield statements.
- Whenever possible, generator must be the preferred method due to its simplicity, while the protocol implementation gives much more control.