Last modified April 8, 2025
This comprehensive guide explores Python's __iter__ method, the
special method that makes objects iterable. We'll cover iteration protocols,
custom iterators, generator functions, and practical examples.
The __iter__ method returns an iterator object that implements the
iteration protocol. It is called when an object needs to be iterated over, such
as in a for loop.
Key characteristics: it must return an iterator object (which implements
__next__), enables iteration over container objects, and works
with Python's built-in iter() function.
Here's a simple implementation showing how __iter__ makes a class
iterable. The example demonstrates the minimal requirements for iteration.
class MyIterable: def __init__(self, data): self.data = data def __iter__(self): return iter(self.data) items = MyIterable([1, 2, 3]) for item in items: print(item)
This example delegates iteration to the built-in list iterator. The
__iter__ method returns an iterator for the internal data list.
The iter() function calls __iter__ under the hood.
This is the standard way to make an object work with for loops.
We can create a custom iterator by implementing both __iter__ and
__next__ methods. This gives full control over iteration behavior.
class CountDown: def __init__(self, start): self.current = start self.start = start def __iter__(self): return self def __next__(self): if self.current < 0: raise StopIteration else: result = self.current self.current -= 1 return result for num in CountDown(5): print(num)
This countdown iterator returns numbers from start down to 0. The
__iter__ returns self since the class is its own iterator.
The __next__ method controls the iteration logic and raises
StopIteration when done. This is required by the iterator protocol.
Instead of a separate iterator class, we can use a generator function in
__iter__. This is often more concise and readable.
class Squares: def __init__(self, limit): self.limit = limit def __iter__(self): for i in range(1, self.limit + 1): yield i * i for square in Squares(5): print(square)
This example generates squares of numbers up to a limit. The __iter__
method uses yield to create a generator iterator automatically.
Generator functions simplify iterator implementation by handling the
__next__ and state management automatically. They're memory
efficient too.
__iter__ is particularly useful for custom data structures where
you want to control how elements are accessed during iteration.
class TreeNode: def __init__(self, value, left=None, right=None): self.value = value self.left = left self.right = right def __iter__(self): if self.left: yield from self.left yield self.value if self.right: yield from self.right tree = TreeNode(4, TreeNode(2, TreeNode(1), TreeNode(3)), TreeNode(6, TreeNode(5), TreeNode(7)) ) for value in tree: print(value) # Prints 1-7 in order
This binary tree implements in-order traversal through __iter__.
The yield from delegates to child nodes' iterators recursively.
This pattern works for any tree-like structure where you want to hide the complexity of traversal behind a simple iteration interface.
__iter__ can implement lazy evaluation, where values are computed
only when needed during iteration, saving memory and CPU cycles.
class Fibonacci: def __iter__(self): a, b = 0, 1 while True: yield a a, b = b, a + b import itertools for fib in itertools.islice(Fibonacci(), 10): print(fib)
This Fibonacci sequence generator computes numbers on demand. The infinite
sequence is safely used with itertools.islice for demonstration.
Lazy evaluation is powerful for large or infinite sequences where precomputing all values would be impractical or impossible.
__iter__ call should return a fresh iterator__iter__ and __next__StopIterationMy name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.
List all Python tutorials.