__iter__ and __next__

RoadmapsPython

Overview

The `__iter__` and `__next__` dunder methods form the foundation of Python's iteration protocol. Just like a bookmark in a book, the iterator keeps track of your current page. To support this, your class must implement two methods: - `__iter__(self)`: Initializes or returns the iterator instance itself. Returns an object with a `__next__` method (usually `self`). - `__next__(self)`: Advances the sequence to the next state. Returns the next item, or raises `StopIteration` when the sequence is exhausted. class CountUp: 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 self.current += 1 return self.current A common trap is trying to reuse an iterator after it's exhausted. Because `__next__` mutates internal state until it raises `StopIteration`, a second `for` loop will instantly terminate. counter = CountUp(2) list(counter) # [1, 2] list(counter) # [] - Exhausted! This single-pass behavior is a key feature of the protocol, covered fully in `iterator-protocol`.

They allow custom objects to behave like built-in sequences (e.g., lists or strings) in `for` loops, comprehensions, and functions like `sum()` or `list()`.

Where used: Custom data structures, Database cursors, Streaming APIs

Why learn this

Code walkthrough

class Counter:
  def __init__(self, limit):
    self.limit = limit
    self.val = 0
  def __iter__(self):
    return self
  def __next__(self):
    if self.val >= self.limit:
      raise StopIteration
    self.val += 1
    return self.val

for num in Counter(2):
  print(num)

Focus: if self.val >= self.limit: raise StopIteration

Aha moment

class Single:
  def __iter__(self):
    return self
  def __next__(self):
    return 42

print(next(iter(Single())))

Prediction: What does this code print?

Common guess: An error, because there is no __init__ method or StopIteration.

It prints 42. You don't actually need state or `StopIteration` if you just want an infinite sequence. `iter()` calls `__iter__`, and `next()` calls `__next__` exactly once.

Common mistakes

Glossary

iteration protocol
A specific set of rules in Python that an object must follow to be used in a loop. Example: implementing `__iter__` and `__next__` fulfills the iteration protocol.
seamlessly
Working smoothly and perfectly together without any awkward transitions or extra effort required. Example: a custom class working seamlessly in a standard `for` loop.

Recall questions

Understanding checks

What will happen when you run this code?

It will print `1` infinitely.

The `__next__` method never raises `StopIteration`, so the `for` loop has no signal to stop and will continue indefinitely.

This iterator is supposed to immediately stop when looped over, but it crashes with a `TypeError` when used in a `for` loop. What is the bug?

The class is missing the `__iter__` method.

A `for` loop always calls `iter()` on the object first, which expects an `__iter__` method to return an iterator. Since `OneTime` only defines `__next__`, it cannot be iterated over.

Practice tasks

Fix the Infinite Loop

The given `Countdown` iterator correctly implements `__iter__` and `__next__`, but it counts down forever into negative numbers. Modify the `__next__` method to raise `StopIteration` when `self.current` reaches `0`.

Challenge

A Repeating Sequence

Create a `Repeater` class initialized with a single value and a maximum count. It should yield that same value `count` times when iterated over. For example, `Repeater('A', 3)` should yield `'A'`, `'A'`, `'A'` and then stop. Implement this using `__iter__` and `__next__`.

Continue learning

Previous: Everything is an object in Python

Previous: __init__ and __new__

Next: The Iterable Protocol

Next: Iterator Protocol and StopIteration

Return to Python Roadmap