Skip to content

Iterator Pattern

Category: Behavioral Pattern

Overview

Provide a way to access elements of an aggregate object sequentially without exposing its underlying representation. This pattern decouples the traversal logic from the collection, allowing different traversal algorithms while keeping the collection interface simple.

Usage Guidelines

Use when:

  • Need to access collection elements sequentially
  • Want different ways to traverse same collection
  • Want uniform interface for traversing different collections
  • Internal collection structure should be hidden

Avoid when:

  • Built-in iteration is sufficient for simple collections
  • Need random access to elements
  • Only one way to traverse collection exists
  • Python's built-in iteration protocol works well

Implementation

from __future__ import annotations
from typing import Iterator as TypingIterator

class Book:
    """Represents a book in the library."""

    def __init__(self, title: str, author: str) -> None:
        """Initialize a book."""
        self.title = title
        self.author = author

    def __str__(self) -> str:
        """String representation of the book."""
        return f"{self.title} by {self.author}"

class BookIterator:
    """Concrete iterator for books."""

    def __init__(self, books: list[Book]) -> None:
        """Initialize the iterator."""
        self._books = books
        self._index = 0

    def has_next(self) -> bool:
        """Check if more books exist."""
        return self._index < len(self._books)

    def next(self) -> Book:
        """Get next book."""
        if not self.has_next():
            raise StopIteration("No more books")

        book = self._books[self._index]
        self._index += 1
        return book

class BookCollection:
    """Concrete aggregate of books implementing Python iterator protocol."""

    def __init__(self) -> None:
        """Initialize an empty book collection."""
        self._books: list[Book] = []

    def add_book(self, book: Book) -> None:
        """Add a book to the collection."""
        self._books.append(book)

    def create_iterator(self) -> BookIterator:
        """Create an iterator for this collection."""
        return BookIterator(self._books)

    def __iter__(self) -> TypingIterator[Book]:
        """Make collection iterable using Python's iterator protocol."""
        return iter(self._books)

    def __len__(self) -> int:
        """Get collection length."""
        return len(self._books)

Usage

# Book collection with Python's iterator protocol
library = BookCollection()
library.add_book(Book("Design Patterns", "Gang of Four"))
library.add_book(Book("Clean Code", "Robert Martin"))
library.add_book(Book("Refactoring", "Martin Fowler"))

# Using Python's for loop
for book in library:
    print(book)
# Output:
# Design Patterns by Gang of Four
# Clean Code by Robert Martin
# Refactoring by Martin Fowler

# Using custom iterator
iterator = library.create_iterator()
while iterator.has_next():
    book = iterator.next()
    print(book.title)

Trade-offs

Benefits:

  1. Separates traversal logic from collection (Single Responsibility Principle)
  2. Multiple traversals can happen simultaneously
  3. Same interface for different collections provides uniformity
  4. Collection internals remain hidden through encapsulation

Drawbacks:

  1. Overkill for simple collections
  2. Additional abstraction adds performance overhead
  3. Iterator must track traversal state
  4. Modifying collection during iteration can cause problems

Real-World Examples

  • Database result sets iterating through query results
  • File systems traversing directories and files
  • DOM traversal walking through HTML/XML trees
  • Graph traversal with BFS, DFS algorithms
  • Composite
  • Factory Method
  • Memento
  • Visitor

API Reference

design_patterns.behavioral.iterator

Iterator Pattern Module

The Iterator pattern provides a way to access elements of an aggregate object sequentially without exposing its underlying representation. It decouples the traversal logic from the collection, allowing different traversal algorithms.

Example

Iterating through a custom collection:

library = BookCollection()
library.add_book(Book("Design Patterns", "GoF"))
library.add_book(Book("Clean Code", "Robert Martin"))

for book in library:
    print(book.title)

Aggregate

Bases: ABC

Abstract aggregate interface.

Source code in src/design_patterns/behavioral/iterator.py
class Aggregate(ABC):
    """Abstract aggregate interface."""

    @abstractmethod
    def create_iterator(self) -> Iterator:
        """Create an iterator for this aggregate.

        Returns:
            An iterator instance.
        """
        pass

create_iterator() abstractmethod

Create an iterator for this aggregate.

Returns:

Type Description
Iterator

An iterator instance.

Source code in src/design_patterns/behavioral/iterator.py
@abstractmethod
def create_iterator(self) -> Iterator:
    """Create an iterator for this aggregate.

    Returns:
        An iterator instance.
    """
    pass

BinaryTree

Binary tree with custom iterator.

Source code in src/design_patterns/behavioral/iterator.py
class BinaryTree:
    """Binary tree with custom iterator."""

    def __init__(self) -> None:
        """Initialize an empty binary tree."""
        self.root: TreeNode | None = None

    def insert(self, value: int) -> None:
        """Insert a value into the tree.

        Args:
            value: Value to insert.
        """
        if self.root is None:
            self.root = TreeNode(value)
        else:
            self._insert_recursive(self.root, value)

    def _insert_recursive(self, node: TreeNode, value: int) -> None:
        """Recursively insert value.

        Args:
            node: Current node.
            value: Value to insert.
        """
        if value < node.value:
            if node.left is None:
                node.left = TreeNode(value)
            else:
                self._insert_recursive(node.left, value)
        else:
            if node.right is None:
                node.right = TreeNode(value)
            else:
                self._insert_recursive(node.right, value)

    def create_iterator(self) -> InOrderIterator:
        """Create an in-order iterator.

        Returns:
            In-order iterator.
        """
        return InOrderIterator(self.root)

__init__()

Initialize an empty binary tree.

Source code in src/design_patterns/behavioral/iterator.py
def __init__(self) -> None:
    """Initialize an empty binary tree."""
    self.root: TreeNode | None = None

create_iterator()

Create an in-order iterator.

Returns:

Type Description
InOrderIterator

In-order iterator.

Source code in src/design_patterns/behavioral/iterator.py
def create_iterator(self) -> InOrderIterator:
    """Create an in-order iterator.

    Returns:
        In-order iterator.
    """
    return InOrderIterator(self.root)

insert(value)

Insert a value into the tree.

Parameters:

Name Type Description Default
value int

Value to insert.

required
Source code in src/design_patterns/behavioral/iterator.py
def insert(self, value: int) -> None:
    """Insert a value into the tree.

    Args:
        value: Value to insert.
    """
    if self.root is None:
        self.root = TreeNode(value)
    else:
        self._insert_recursive(self.root, value)

Book

Represents a book in the library.

Source code in src/design_patterns/behavioral/iterator.py
class Book:
    """Represents a book in the library."""

    def __init__(self, title: str, author: str) -> None:
        """Initialize a book.

        Args:
            title: Book title.
            author: Book author.
        """
        self.title = title
        self.author = author

    def __str__(self) -> str:
        """String representation of the book.

        Returns:
            Book details.
        """
        return f"{self.title} by {self.author}"

__init__(title, author)

Initialize a book.

Parameters:

Name Type Description Default
title str

Book title.

required
author str

Book author.

required
Source code in src/design_patterns/behavioral/iterator.py
def __init__(self, title: str, author: str) -> None:
    """Initialize a book.

    Args:
        title: Book title.
        author: Book author.
    """
    self.title = title
    self.author = author

__str__()

String representation of the book.

Returns:

Type Description
str

Book details.

Source code in src/design_patterns/behavioral/iterator.py
def __str__(self) -> str:
    """String representation of the book.

    Returns:
        Book details.
    """
    return f"{self.title} by {self.author}"

BookCollection

Bases: Aggregate

Concrete aggregate of books implementing Python iterator protocol.

Source code in src/design_patterns/behavioral/iterator.py
class BookCollection(Aggregate):
    """Concrete aggregate of books implementing Python iterator protocol."""

    def __init__(self) -> None:
        """Initialize an empty book collection."""
        self._books: list[Book] = []

    def add_book(self, book: Book) -> None:
        """Add a book to the collection.

        Args:
            book: Book to add.
        """
        self._books.append(book)

    def remove_book(self, book: Book) -> None:
        """Remove a book from the collection.

        Args:
            book: Book to remove.
        """
        if book in self._books:
            self._books.remove(book)

    def get_book(self, index: int) -> Book:
        """Get book at index.

        Args:
            index: Book index.

        Returns:
            Book at the specified index.
        """
        return self._books[index]

    def size(self) -> int:
        """Get number of books.

        Returns:
            Number of books in collection.
        """
        return len(self._books)

    def create_iterator(self) -> BookIterator:
        """Create an iterator for this collection.

        Returns:
            Book iterator.
        """
        return BookIterator(self._books)

    def __iter__(self) -> TypingIterator[Book]:
        """Make collection iterable using Python's iterator protocol.

        Returns:
            Iterator over books.
        """
        return iter(self._books)

    def __len__(self) -> int:
        """Get collection length.

        Returns:
            Number of books.
        """
        return len(self._books)

__init__()

Initialize an empty book collection.

Source code in src/design_patterns/behavioral/iterator.py
def __init__(self) -> None:
    """Initialize an empty book collection."""
    self._books: list[Book] = []

__iter__()

Make collection iterable using Python's iterator protocol.

Returns:

Type Description
Iterator[Book]

Iterator over books.

Source code in src/design_patterns/behavioral/iterator.py
def __iter__(self) -> TypingIterator[Book]:
    """Make collection iterable using Python's iterator protocol.

    Returns:
        Iterator over books.
    """
    return iter(self._books)

__len__()

Get collection length.

Returns:

Type Description
int

Number of books.

Source code in src/design_patterns/behavioral/iterator.py
def __len__(self) -> int:
    """Get collection length.

    Returns:
        Number of books.
    """
    return len(self._books)

add_book(book)

Add a book to the collection.

Parameters:

Name Type Description Default
book Book

Book to add.

required
Source code in src/design_patterns/behavioral/iterator.py
def add_book(self, book: Book) -> None:
    """Add a book to the collection.

    Args:
        book: Book to add.
    """
    self._books.append(book)

create_iterator()

Create an iterator for this collection.

Returns:

Type Description
BookIterator

Book iterator.

Source code in src/design_patterns/behavioral/iterator.py
def create_iterator(self) -> BookIterator:
    """Create an iterator for this collection.

    Returns:
        Book iterator.
    """
    return BookIterator(self._books)

get_book(index)

Get book at index.

Parameters:

Name Type Description Default
index int

Book index.

required

Returns:

Type Description
Book

Book at the specified index.

Source code in src/design_patterns/behavioral/iterator.py
def get_book(self, index: int) -> Book:
    """Get book at index.

    Args:
        index: Book index.

    Returns:
        Book at the specified index.
    """
    return self._books[index]

remove_book(book)

Remove a book from the collection.

Parameters:

Name Type Description Default
book Book

Book to remove.

required
Source code in src/design_patterns/behavioral/iterator.py
def remove_book(self, book: Book) -> None:
    """Remove a book from the collection.

    Args:
        book: Book to remove.
    """
    if book in self._books:
        self._books.remove(book)

size()

Get number of books.

Returns:

Type Description
int

Number of books in collection.

Source code in src/design_patterns/behavioral/iterator.py
def size(self) -> int:
    """Get number of books.

    Returns:
        Number of books in collection.
    """
    return len(self._books)

BookIterator

Bases: Iterator

Concrete iterator for books.

Source code in src/design_patterns/behavioral/iterator.py
class BookIterator(Iterator):
    """Concrete iterator for books."""

    def __init__(self, books: list[Book]) -> None:
        """Initialize the iterator.

        Args:
            books: List of books to iterate.
        """
        self._books = books
        self._index = 0

    def has_next(self) -> bool:
        """Check if more books exist.

        Returns:
            True if more books exist.
        """
        return self._index < len(self._books)

    def next(self) -> Book:
        """Get next book.

        Returns:
            The next book.

        Raises:
            StopIteration: When no more books exist.
        """
        if not self.has_next():
            raise StopIteration("No more books")

        book = self._books[self._index]
        self._index += 1
        return book

__init__(books)

Initialize the iterator.

Parameters:

Name Type Description Default
books list[Book]

List of books to iterate.

required
Source code in src/design_patterns/behavioral/iterator.py
def __init__(self, books: list[Book]) -> None:
    """Initialize the iterator.

    Args:
        books: List of books to iterate.
    """
    self._books = books
    self._index = 0

has_next()

Check if more books exist.

Returns:

Type Description
bool

True if more books exist.

Source code in src/design_patterns/behavioral/iterator.py
def has_next(self) -> bool:
    """Check if more books exist.

    Returns:
        True if more books exist.
    """
    return self._index < len(self._books)

next()

Get next book.

Returns:

Type Description
Book

The next book.

Raises:

Type Description
StopIteration

When no more books exist.

Source code in src/design_patterns/behavioral/iterator.py
def next(self) -> Book:
    """Get next book.

    Returns:
        The next book.

    Raises:
        StopIteration: When no more books exist.
    """
    if not self.has_next():
        raise StopIteration("No more books")

    book = self._books[self._index]
    self._index += 1
    return book

InOrderIterator

Bases: Iterator

Iterator for in-order tree traversal.

Source code in src/design_patterns/behavioral/iterator.py
class InOrderIterator(Iterator):
    """Iterator for in-order tree traversal."""

    def __init__(self, root: TreeNode | None) -> None:
        """Initialize in-order iterator.

        Args:
            root: Root of the tree.
        """
        self._stack: list[TreeNode] = []
        self._current = root
        self._push_left_nodes(root)

    def _push_left_nodes(self, node: TreeNode | None) -> None:
        """Push all left nodes onto stack.

        Args:
            node: Starting node.
        """
        while node:
            self._stack.append(node)
            node = node.left

    def has_next(self) -> bool:
        """Check if more nodes exist.

        Returns:
            True if more nodes exist.
        """
        return len(self._stack) > 0

    def next(self) -> int:
        """Get next node value in in-order traversal.

        Returns:
            Next node value.

        Raises:
            StopIteration: When no more nodes exist.
        """
        if not self.has_next():
            raise StopIteration("No more nodes")

        node = self._stack.pop()
        value = node.value

        if node.right:
            self._push_left_nodes(node.right)

        return value

__init__(root)

Initialize in-order iterator.

Parameters:

Name Type Description Default
root TreeNode | None

Root of the tree.

required
Source code in src/design_patterns/behavioral/iterator.py
def __init__(self, root: TreeNode | None) -> None:
    """Initialize in-order iterator.

    Args:
        root: Root of the tree.
    """
    self._stack: list[TreeNode] = []
    self._current = root
    self._push_left_nodes(root)

has_next()

Check if more nodes exist.

Returns:

Type Description
bool

True if more nodes exist.

Source code in src/design_patterns/behavioral/iterator.py
def has_next(self) -> bool:
    """Check if more nodes exist.

    Returns:
        True if more nodes exist.
    """
    return len(self._stack) > 0

next()

Get next node value in in-order traversal.

Returns:

Type Description
int

Next node value.

Raises:

Type Description
StopIteration

When no more nodes exist.

Source code in src/design_patterns/behavioral/iterator.py
def next(self) -> int:
    """Get next node value in in-order traversal.

    Returns:
        Next node value.

    Raises:
        StopIteration: When no more nodes exist.
    """
    if not self.has_next():
        raise StopIteration("No more nodes")

    node = self._stack.pop()
    value = node.value

    if node.right:
        self._push_left_nodes(node.right)

    return value

Iterator

Bases: ABC

Abstract iterator interface.

Source code in src/design_patterns/behavioral/iterator.py
class Iterator(ABC):
    """Abstract iterator interface."""

    @abstractmethod
    def has_next(self) -> bool:
        """Check if there are more elements.

        Returns:
            True if more elements exist.
        """
        pass

    @abstractmethod
    def next(self) -> Any:
        """Get the next element.

        Returns:
            The next element.

        Raises:
            StopIteration: When no more elements exist.
        """
        pass

has_next() abstractmethod

Check if there are more elements.

Returns:

Type Description
bool

True if more elements exist.

Source code in src/design_patterns/behavioral/iterator.py
@abstractmethod
def has_next(self) -> bool:
    """Check if there are more elements.

    Returns:
        True if more elements exist.
    """
    pass

next() abstractmethod

Get the next element.

Returns:

Type Description
Any

The next element.

Raises:

Type Description
StopIteration

When no more elements exist.

Source code in src/design_patterns/behavioral/iterator.py
@abstractmethod
def next(self) -> Any:
    """Get the next element.

    Returns:
        The next element.

    Raises:
        StopIteration: When no more elements exist.
    """
    pass

Range

Custom range implementation demonstrating iterator pattern.

Source code in src/design_patterns/behavioral/iterator.py
class Range:
    """Custom range implementation demonstrating iterator pattern."""

    def __init__(self, start: int, end: int, step: int = 1) -> None:
        """Initialize range.

        Args:
            start: Start value.
            end: End value (exclusive).
            step: Step size.
        """
        self.start = start
        self.end = end
        self.step = step

    def __iter__(self) -> TypingIterator[int]:
        """Create iterator for the range.

        Returns:
            Range iterator.
        """
        current = self.start
        while current < self.end:
            yield current
            current += self.step

    def __len__(self) -> int:
        """Calculate range length.

        Returns:
            Number of elements in range.
        """
        return max(0, (self.end - self.start + self.step - 1) // self.step)

__init__(start, end, step=1)

Initialize range.

Parameters:

Name Type Description Default
start int

Start value.

required
end int

End value (exclusive).

required
step int

Step size.

1
Source code in src/design_patterns/behavioral/iterator.py
def __init__(self, start: int, end: int, step: int = 1) -> None:
    """Initialize range.

    Args:
        start: Start value.
        end: End value (exclusive).
        step: Step size.
    """
    self.start = start
    self.end = end
    self.step = step

__iter__()

Create iterator for the range.

Returns:

Type Description
Iterator[int]

Range iterator.

Source code in src/design_patterns/behavioral/iterator.py
def __iter__(self) -> TypingIterator[int]:
    """Create iterator for the range.

    Returns:
        Range iterator.
    """
    current = self.start
    while current < self.end:
        yield current
        current += self.step

__len__()

Calculate range length.

Returns:

Type Description
int

Number of elements in range.

Source code in src/design_patterns/behavioral/iterator.py
def __len__(self) -> int:
    """Calculate range length.

    Returns:
        Number of elements in range.
    """
    return max(0, (self.end - self.start + self.step - 1) // self.step)

TreeNode

Node in a binary tree.

Source code in src/design_patterns/behavioral/iterator.py
class TreeNode:
    """Node in a binary tree."""

    def __init__(self, value: int) -> None:
        """Initialize a tree node.

        Args:
            value: Node value.
        """
        self.value = value
        self.left: TreeNode | None = None
        self.right: TreeNode | None = None

__init__(value)

Initialize a tree node.

Parameters:

Name Type Description Default
value int

Node value.

required
Source code in src/design_patterns/behavioral/iterator.py
def __init__(self, value: int) -> None:
    """Initialize a tree node.

    Args:
        value: Node value.
    """
    self.value = value
    self.left: TreeNode | None = None
    self.right: TreeNode | None = None