Skip to main content

Iterators: How for Really Works

intermediate13 min readLesson 67 of 169

The iteration protocol: iter(), next(), StopIteration, and custom iterators.

for x in thing is not magic. Python asks thing for an iterator (iter(thing)), then calls next() on it repeatedly until StopIteration:

nums = [10, 20]
it = iter(nums)      # an iterator object
next(it)             # 10
next(it)             # 20
next(it)             # raises StopIteration โ€” the for-loop's exit signal

Any object with __iter__ returning an iterator is iterable; an iterator carries __next__. Lists, dicts, files, ranges โ€” all just implement this one protocol. That's why for works identically over all of them.

Your own iterator

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

    def __iter__(self):
        return self            # the object IS its own iterator

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

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

A subtlety worth knowing: an iterator is exhausted after one pass โ€” a second for over the same iterator sees nothing. Iterables (like lists) produce a fresh iterator each time; that's why you can loop over a list twice but not over a generator twice.

Now practice

Iterator DrillsImplement the protocol by hand, once, so you never fear it again.2 challenges ยท ยท ~25 min