Iterators in Python
Explore how iterators work in Python by implementing __iter__ and __next__ magic methods. Understand how to create custom iterator classes, manage infinite loops, and use the StopIteration exception for controlled iteration. This lesson deepens your knowledge of Python's iteration protocol and compatibility differences between Python 2 and 3.
We'll cover the following...
In the previous lesson, we pondered over the decency of the for loop. Now let’s unveil it and see the magic behind it.
What is an iterator?
In Python, an iterator is an object that implements the __iter__ and __next__ dunder methods.
⚙️ Note: Dunder methods are methods having two prefix and suffix underscores in the method name. They are also known as magic methods. For example:
__init__,__len__,__repr__, etc.
The __iter__() function returns an iterator object that goes through each element of the given object. Whereas, the __next__() function returns the next item in the sequence.
Objects that support the __iter__ and __next__ methods automatically work with loops.
Iterator from scratch
To create an iterator class, it must implement the __iter__() and __next() methods. Let’s create an iterator that returns numbers.
The above snippet makes it clear that the Counter will iterate over the sequence of infinite numbers starting from 1.
Stopping the iteration
The above executable will continue returning numbers forever if next() statements are used boundlessly, or if an infinite for loop is applied.
for x in iterator:
print(x)
But, who wants to iterate forever? To halt the iteration, we can use the StopIteration exception by adding a termination condition in the __next__() method.
❌ Note: If we call the iterator after all the elements have been iterated, then
StopIterationErroris raised.
Now, let’s add a termination condition:
We add a termination condition at line 9. If that condition is False, it directly raises the exception StopIteration from line 14. So, now you see the loop itself breaks when n exceeds the value of 10.
🧐 You may have noticed something different. We can add the
__init__()function and shiftself.n(the initialization statement) from__iter__()to__init__(), leaving__iter__()with thereturnstatement only.
Python 2.x compatibility
There’s a slight difference when implementing iterators in Python 2 and Python3.
In Python 3, the next value from the iterator is received via the __next__() method. But, in Python 2, it’s done with next() method (without any underscores).