Iterator Pattern

Sean Bradley
Design Patterns In Python
4 min readAug 17, 2020

--

The Iterator will commonly contain two methods that perform the following concepts.

  • next: returns the next object in the aggregate (collection, object).
  • has_next: returns a Boolean indicating if the Iterable is at the end of the iteration or not.

The benefits of using the Iterator pattern are that the client can traverse a collection of aggregates(objects) without needing to understand their internal representations and/or data structures.

Terminology

  • Iterator Interface: The Interface for an object to implement.
  • Concrete Iterator: (Iterable) The instantiated object that implements the iterator and contains a collection of aggregates.
  • Aggregate Interface: An interface for defining an aggregate (object).
  • Concrete Aggregate: The object that implements the Aggregate interface.

Iterator Pattern UML Diagram

Iterator Pattern UML Diagram

Source Code

In this concept example, I create 4 objects called Aggregate and group them into a collection.

They are very minimal objects that implement one method that prints a line.

I then create an Iterable and pass in the collection of Aggregates.

I can now traverse the aggregates through the Iterable interface.

./iterator/iterator_concept.py

"""
The Iterator Pattern Concept
https://sbcode.net/python/iterator/#iteratoriterator_conceptpy
"""
from abc import ABCMeta, abstractmethodclass IIterator(metaclass=ABCMeta):
"An Iterator Interface"
@staticmethod
@abstractmethod
def has_next():
"Returns Boolean whether at end of collection or not"
@staticmethod
@abstractmethod
def next():
"Return the object in collection"
class Iterable(IIterator):
"The concrete iterator (iterable)"
def __init__(self, aggregates):
self.index = 0
self.aggregates = aggregates
def next(self):
if self.index < len(self.aggregates):
aggregate = self.aggregates[self.index]
self.index += 1
return aggregate
raise Exception("AtEndOfIteratorException", "At End of Iterator")
def has_next(self):
return self.index < len(self.aggregates)
class IAggregate(metaclass=ABCMeta):
"An interface that the aggregates should implement"
@staticmethod
@abstractmethod
def method():
"a method to implement"
class Aggregate(IAggregate):
"A concrete object"
@staticmethod
def method():
print("This method has been invoked")
# The Client
AGGREGATES = [Aggregate(), Aggregate(), Aggregate(), Aggregate()]
# AGGREGATES is a python list that is already iterable by default.
# but we can create own own iterator on top anyway.
ITERABLE = Iterable(AGGREGATES)
while ITERABLE.has_next():
ITERABLE.next().method()

Output

python ./iterator/iterator_concept.py
This method has been invoked
This method has been invoked
This method has been invoked
This method has been invoked

Video of the Iterator Pattern

Example Use Case

Visit Iterator — Design Patterns In Python (sbcode.net) for an example use case of the Iterator pattern.

One reason for not using the inbuilt Python data structures that implement iterators already, or using the iter function directly over an existing collection, is in the case when you want to create an object that can dynamically create iterated objects, you want a custom order of objects or an infinite iterator.

The iterator in the use case example will return the next number in the iterator multiplied by 2 modulus 11. It dynamically creates the returned object (number) at runtime.

It has no has_next() method since the result is modulated by 11, that will loop the results no matter how large the iterator index is. It will also appear to alternate between a series of even numbers and odd numbers.

Also, just to demonstrate that implementing abstract classes and interfaces is not always necessary, this example uses no abstract base classes or interfaces.

Iterator Use Case Example UML Diagram

Video of an Iterator Pattern use case

Summary

  • There are many ways to create Iterators. They are already built into Python and used instead of creating custom classes.
  • Use an iterator when you need to traverse over a collection, or you want an object that can output a series of dynamically created objects.
  • At minimum, an iterator needs a next equivalent method that returns an object.
  • Optionally you can also create a helper function that indicates whether an iterator is at the end or not. This is useful if you use your iterator in a while loop.
  • Alternatively, use the sentinel option of the Python iter() method to indicate the last iteration. Note that the Iterator object needs to be callable. Set the __call__ reference to its next method.

Thankyou for reading my quick tutorial on the Iterator Design Pattern. For more design patterns please visit my series on Design Patterns in Python at https://medium.com/design-patterns-in-python

Sean

Design Patterns In Python (Book)

You can also buy this series in paperback.
Book provides FREE access to online instructional videos.
Design Patterns In Python : ASIN B08XLJ8Z2J

--

--

Sean Bradley
Design Patterns In Python

Developer of real time, low latency, high availability, asynchronous, multi threaded, remotely managed, fully automated and monitored solutions.