Introduction
In Design Patterns, the Iterator is a way of traversing over a container, that is access each of the container’s elements. It is used as a convenient way to traverse over lists or arrays, but it could also be used to traverse over binary trees for example.
It looks like this:
A bit of explanation can be handy:
- An iterator basically has two methods: next() and hasNext()
- The next() method returns the next element in the container
- The hasNext() method returns a bool, true if a next element is available, false if there is no next element.
Implementation in Python
Like many modern languages, iterators are built into the language itself, lists and tuples for example can be iterated over. For our purpose we will build our own custom iterator, which just yields even numbers, up to a certain limit. You will see it will behave just like any other iterable type.
Since we want to return the type of the enclosing class, we need the following import:
from typing import Self
You will find an excellent article on this right here.
The EvenNumber iterator looks like this:
class EvenNumbers:
_limit: int = None
_current: int = None
def __init__(self, limit: int):
self._limit = limit
self._current = 0
def __iter__(self) -> Self:
return self
def __next__(self) -> int:
self._current += 2
if self._current <= self._limit:
return self._current
else:
raise StopIteration
Some notes on this code:
- In the constructor we set the starting value to 0, and we set a limit
- The __iter__() method returns the iterator, in our case just self
- In the __next__() method we return the next element in the sequence, if there is none, we raise a StopIteration exception and the iterator stops.
Using this iterator is almost ridiculously easy:
if __name__=="__main__":
for i in EvenNumbers(40):
print(i, end=" ")
Here we just construct an object of the EvenNumbers class and set a limit. After that we simply iterate over the object, and all the method calls are done implicitly.
Conclusion
As you can see, as with many things in Python, it is very easy to build a simple iterator. My advice however would be that whenever you can, use Python’s built-in iterators.
One possible extension of this iterator would be to make it multi-threaded, but that will be the subject of another post. Also iterating over a somewhat more complicated datastructure would be an interesting application for this pattern, but that will also have to wait for another post.