iterators
programming
coding tutorial
Python
software development

How to build a basic iterator?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In Python, an iterator is an object that produces values one at a time and remembers where it is between calls. Building a basic iterator is mostly about understanding the iterator protocol: implement __iter__ and __next__, and raise StopIteration when the sequence is exhausted.

The Iterator Protocol

A Python iterator must provide:

  • '__iter__(), which returns the iterator object'
  • '__next__(), which returns the next value or raises StopIteration'

That is the whole protocol. Once those methods are present, the object works with for, next, list conversion, and many other Python tools.

A Minimal Custom Iterator

python
1class CountUpTo:
2    def __init__(self, limit):
3        self.limit = limit
4        self.current = 1
5
6    def __iter__(self):
7        return self
8
9    def __next__(self):
10        if self.current > self.limit:
11            raise StopIteration
12
13        value = self.current
14        self.current += 1
15        return value
16
17
18counter = CountUpTo(3)
19for number in counter:
20    print(number)

This prints:

text
1
2
3

The iterator stores its progress in self.current, so each call to __next__ resumes where the previous one ended.

Use next() Directly

You do not have to rely on a for loop. You can consume the iterator manually:

python
1counter = CountUpTo(2)
2it = iter(counter)
3
4print(next(it))
5print(next(it))

If you call next(it) again after exhaustion, Python raises StopIteration.

That behavior is exactly what for loops rely on internally. It is also a useful debugging trick when you want to inspect iterator state step by step instead of consuming the whole sequence at once.

Iterator Versus Iterable

This distinction matters:

  • an iterable can produce an iterator
  • an iterator is the stateful object that yields one item at a time

A list is iterable:

python
data = [10, 20, 30]
it = iter(data)
print(next(it))

The list itself is not the iterator state machine. The object returned by iter(data) is.

In the earlier CountUpTo example, the class acts as both iterable and iterator because __iter__ returns self.

When One Object Should Not Reuse Its Own State

Sometimes you want an iterable that creates a fresh iterator each time.

python
1class CountRange:
2    def __init__(self, limit):
3        self.limit = limit
4
5    def __iter__(self):
6        return CountUpTo(self.limit)
7
8
9numbers = CountRange(3)
10print(list(numbers))
11print(list(numbers))

This works twice because each iteration gets a new CountUpTo instance. That is often a better design than reusing one mutable iterator object when the iterable may be consumed multiple times.

Generator Functions Are Often Simpler

For many cases, a generator is the cleaner way to build iterator behavior:

python
1def count_up_to(limit):
2    current = 1
3    while current <= limit:
4        yield current
5        current += 1
6
7
8for number in count_up_to(3):
9    print(number)

Generators still produce iterators, but Python manages the state machine for you. If your goal is only to produce values sequentially, a generator is often the better choice.

Common Pitfalls

The biggest mistake is forgetting to raise StopIteration when the iterator is exhausted. Without that, iteration never terminates correctly.

Another issue is returning a new unrelated object from __iter__ without understanding whether the class is supposed to be an iterator or an iterable that creates iterators.

A third problem is storing iterator state on an object that is expected to support multiple independent iterations, which leads to confusing reuse behavior.

Summary

  • A basic iterator in Python implements __iter__ and __next__.
  • '__next__ must raise StopIteration when no more values remain.'
  • Iterators are stateful; iterables are objects that can produce iterators.
  • A class can be both iterable and iterator, but that is not always the best design.
  • Generator functions are often a simpler way to create iterator behavior.

Course illustration
Course illustration

All Rights Reserved.