Easy Python Patterns: The Iterator

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:

  1. An iterator basically has two methods: next() and hasNext()
  2. The next() method returns the next element in the container
  3. 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:
    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 print out the results, and all the method calls are done implicitly.

Conclusion

The Iterator design pattern provides a powerful and elegant solution for decoupling the traversal logic from the underlying collection. As demonstrated by the custom EvenNumbers class, the pattern allows you to access the elements of an aggregate object sequentially without exposing its internal structure.

In Python, the spirit of the Iterator pattern is deeply embedded in the language through the iterable protocol, which relies on the magic methods __iter__() and __next__(). By implementing these methods, we were able to create a class that behaves exactly like built-in iterable types, seamlessly integrating with the standard for loop syntax. This not only makes the code cleaner and more readable but also adheres to the principle of “fail fast” by using the StopIteration exception to signal the end of traversal.

Ultimately, whether custom-built or leveraging built-in language features, the Iterator pattern promotes flexibility, code reusability, and separation of concerns, making it an indispensable tool in object-oriented programming for managing and traversing data collections of any complexity.

The Code Nomad
The Code Nomad
Articles: 165

Leave a Reply

Your email address will not be published. Required fields are marked *