How to Implement the Iterator Protocol in Python

A manual iterator class using __iter__ and __next__, compared with an equivalent generator using yield.

Easy Python 3.9+ Aug 9, 2026 Comprehensions & generators 13 views 0 copies

Python code

27 lines
Python 3.9+
class ManualCounter:
    def __init__(self, limit):
        self.limit = limit
        self.current = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.current >= self.limit:
            raise StopIteration
        value = self.current
        self.current += 1
        return value


def generator_counter(limit):
    for i in range(limit):
        yield i


if __name__ == "__main__":
    manual = ManualCounter(5)
    print("Manual iterator:", list(manual))

    gen = generator_counter(5)
    print("Generator:", list(gen))

Output

stdout
Manual iterator: [0, 1, 2, 3, 4]
Generator: [0, 1, 2, 3, 4]

How it works

The ManualCounter class follows the iterator protocol by defining __iter__ (returning itself) and __next__ (returning the next value or raising StopIteration when done). list(manual) repeatedly calls __next__ until the exception is raised. The generator_counter function uses yield instead — each call to next() resumes execution right after the last yield. Both produce identical sequences, but the generator version is shorter and handles state automatically. range() is already an iterable, so iterating over it in the generator keeps memory constant regardless of limit.

Common mistakes

  • Forgetting to implement __iter__ when defining a custom iterator class
  • Mutating the iterable while iterating over it
  • Raising StopIteration from inside a generator instead of using return
  • Assuming an iterator is reusable — list(manual) can only be consumed once

Variations

  1. Use iter() and next() builtins explicitly to step through a manual iterator
  2. Convert a manual iterator to an iterator of tuples with zip(*[iter(obj)]*n) for batching

Real-world use cases

  • Building a custom streaming reader that yields database rows one at a time.
  • Implementing an infinite or stateful sequence, such as a pseudo-random number generator.
  • Creating a lazy wrapper around an expensive paginated API response.

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Comprehensions & generators

Related tutorials and quizzes for this topic.