pyguides

__iter__

__iter__(self)

__iter__ is called whenever Python needs to iterate over an object. It returns an iterator — typically self, but potentially any object with a __next__ method. Without __iter__, Python falls back to __getitem__ if available, using sequential integer indices until an IndexError.

Signature

def __iter__(self):
    return self  # most common: object is its own iterator

__iter__ takes only self. It should return an iterator object. The returned object is what for loops, list(), set(), and other iteration consumers actually iterate over.

Quick example

class Countdown:
    def __init__(self, start):
        self.current = start

    def __iter__(self):
        return self

    def __next__(self):
        if self.current < 0:
            raise StopIteration
        value = self.current
        self.current -= 1
        return value

for n in Countdown(3):
    print(n)
# Output: 3
# Output: 2
# Output: 1
# Output: 0

The for loop calls iter(Countdown(3)), which calls Countdown(3).__iter__(). That returns self, so the loop then calls __next__ on the same object until it raises StopIteration.

Iterable vs iterator

These two terms mean different things:

  • Iterable: an object with __iter__. You can call iter() on it.
  • Iterator: an object with both __iter__ and __next__. It produces items one at a time.

Many objects are both at once — they implement __iter__ returning self and __next__ producing items. But you can also split the roles:

class Book:
    def __init__(self, pages):
        self.pages = pages

    def __iter__(self):
        return PageIterator(self.pages)

class PageIterator:
    def __init__(self, pages):
        self.pages = pages
        self.index = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.index >= len(self.pages):
            raise StopIteration
        page = self.pages[self.index]
        self.index += 1
        return page

book = Book(["page 1", "page 2", "page 3"])
for page in book:
    print(page)
# page 1
# page 2
# page 3

The Book is an iterable — it has __iter__ but returns a separate PageIterator. The PageIterator is the actual iterator, maintaining state across calls to __next__.

Patterns

Return self for a self-iterating object

The most common pattern. The object is both iterable and iterator:

class Repeater:
    def __init__(self, value, times):
        self.value = value
        self.times = times
        self.count = 0

    def __iter__(self):
        return self

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

for word in Repeater("hi", 3):
    print(word)
# hi
# hi
# hi

Delegate to an internal sequence

When you already have an iterable backing your class:

class Stack:
    def __init__(self):
        self._items = []

    def push(self, item):
        self._items.append(item)

    def __iter__(self):
        return iter(self._items)  # delegate to list's iterator

stack = Stack()
stack.push("a")
stack.push("b")
stack.push("c")

for item in stack:
    print(item)
# a
# b
# c

This is cleaner than manually implementing __next__ — you get iteration for free.

Return a fresh iterator each time

Returning a new iterator object lets you iterate the same collection multiple times simultaneously:

class Grid:
    def __init__(self, rows, cols):
        self.rows = rows
        self.cols = cols

    def __iter__(self):
        return GridIterator(self.rows, self.cols)

class GridIterator:
    def __init__(self, rows, cols):
        self.r = 0
        self.c = 0
        self.rows = rows
        self.cols = cols

    def __iter__(self):
        return self

    def __next__(self):
        if self.r >= self.rows:
            raise StopIteration
        value = (self.r, self.c)
        self.c += 1
        if self.c >= self.cols:
            self.c = 0
            self.r += 1
        return value

g = Grid(2, 3)
for cell in g:
    print(cell)
# (0, 0)
# (0, 1)
# (0, 2)
# (1, 0)
# (1, 1)
# (1, 2)

Where iter gets called

A surprising number of Python constructs call __iter__ indirectly:

ExpressionHow it reaches __iter__
for x in objiter(obj)obj.__iter__()
list(obj)iter(obj)obj.__iter__()
tuple(obj)iter(obj)obj.__iter__()
set(obj)iter(obj)obj.__iter__()
any(obj), all(obj)iter(obj)obj.__iter__()
max(obj), min(obj)iter(obj)obj.__iter__()
sorted(obj)iter(obj)obj.__iter__()
enumerate(obj)iter(obj)obj.__iter__()
zip(a, b)iter(a) and iter(b)

Gotchas

getitem fallback

If an object has no __iter__ but does have __getitem__, Python will iterate using integer indices starting at 0 until IndexError. This is how plain list-like objects worked before the iterator protocol existed:

class OldSchoolSequence:
    def __init__(self, items):
        self.items = items

    def __getitem__(self, index):
        return self.items[index]

# This actually works — __iter__ is not needed
for item in OldSchoolSequence([1, 2, 3]):
    print(item)
# 1
# 2
# 3

If both __iter__ and __getitem__ exist, Python uses __iter__ only.

Non-iterator return type

If __iter__ returns an object without __next__, Python raises TypeError when iteration is attempted:

class BadIterable:
    def __iter__(self):
        return "not an iterator"  # string has no __next__

for x in BadIterable():
    print(x)
# TypeError: 'str' object is not an iterator

Iterators are one-shot

An iterator that returns self from __iter__ is consumed after one pass. The same object reference is exhausted:

counter = Countdown(3)
print(list(counter))   # [3, 2, 1, 0]
print(list(counter))   # [] — already exhausted

If you need reusable iteration, return a fresh iterator from __iter__.

Generators have iter automatically

Generator functions (def ... yield) and generator expressions create objects that already implement __iter__ and __next__ — you don’t need to define either manually:

def fibonacci(n):
    a, b = 0, 1
    for _ in range(n):
        yield a
        a, b = b, a + b

fib = fibonacci(5)
for num in fib:
    print(num)
# 0
# 1
# 1
# 2
# 3

See Also

  • next() — retrieve the next item from an iterator
  • iter() — obtain an iterator from an iterable
  • next — produce the next item in an iterator
  • Understanding Generators — how generators fit into the iteration protocol