Iterators: __iter__ & __next__

The iterator protocol is the simple contract behind every Python loop: an iterable returns an iterator from its __iter__ method, and that iterator hands back one value per call to __next__ until it raises StopIteration to signal the end.

Learn Iterators: __iter__ & __next__ in our free Python course — an interactive lesson with runnable examples, a practice exercise and a quick reference.

Part of the free Python course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

Understanding this protocol demystifies the for-loop, generators, and half the standard library — and lets you build your own loopable objects.

Call iter() on any iterable to get an iterator, then call next() to pull values one at a time. When the items run out, next() raises StopIteration :

To make your own loopable object, implement two dunder methods: __iter__ returns the iterator (usually self ), and __next__ returns the next value or raises StopIteration :

Because the class implements the protocol, every Python construct that loops — for , list() , sum() , comprehensions, unpacking — works on it automatically. That's the power of following the protocol.

A generator function with yield produces an iterator for free — Python writes __iter__ and __next__ behind the scenes. Compare the same countdown both ways:

Replace each ___ so the class is a valid iterator that yields 1, 2, 3 then stops.

Build an iterator that yields the first n Fibonacci numbers using the protocol — a classic that shows off lazy, stateful iteration.

Lesson complete — the for-loop holds no secrets!

You can tell an iterable from an iterator, drive iteration with iter() and next() , handle StopIteration , write a custom iterator class with __iter__ and __next__ , and you know when a generator is the simpler choice.

🚀 Up next: Checkpoint — Data Structures — put it all together in a build challenge.

Practice quiz

What is the difference between an iterable and an iterator?

  • They are identical
  • An iterator is always a list
  • An iterable produces an iterator; an iterator walks through items via __next__
  • An iterable has no __iter__

Answer: An iterable produces an iterator; an iterator walks through items via __next__. An iterable returns an iterator from __iter__; the iterator yields items one at a time via __next__.

Which two dunder methods make up the iterator protocol?

  • __iter__ and __next__
  • __init__ and __call__
  • __get__ and __set__
  • __enter__ and __exit__

Answer: __iter__ and __next__. An iterator implements __iter__ (returns itself) and __next__ (returns the next value).

What does an iterator's __next__ do when there are no items left?

  • Returns None
  • Returns 0
  • Loops forever
  • Raises StopIteration

Answer: Raises StopIteration. __next__ raises StopIteration to signal the end of iteration.

How does a for-loop end when looping over an iterator?

  • It counts the length first
  • It catches StopIteration and stops
  • It checks for None
  • It runs a fixed number of times

Answer: It catches StopIteration and stops. The for-loop repeatedly calls next() and stops when StopIteration is raised.

Given an exhausted iterator it, what does next(it, 'DONE') return?

  • 'DONE'
  • StopIteration
  • None
  • An error

Answer: 'DONE'. next() with a default returns that default instead of raising StopIteration.

After it = iter([1, 2]); list(it), what does a second list(it) give?

An iterator is one-shot; once exhausted it yields nothing, so the second list() is empty.

For the Fibonacci iterator in the lesson, what does list(Fib(10)) produce?

Starting from a=0, b=1, the first 10 Fibonacci numbers are [0, 1, 1, 2, 3, 5, 8, 13, 21, 34].

What should __iter__ return to satisfy the protocol?

  • A list
  • None
  • A string
  • Something with a __next__ method (often self)

Answer: Something with a __next__ method (often self). Returning a non-iterator (like a list) causes TypeError: iter() returned non-iterator; return self or iter(...).

Why is a generator function (using yield) often simpler than a full iterator class?

  • It runs faster always
  • Python writes __iter__ and __next__ for you
  • It needs no StopIteration ever
  • It cannot be exhausted

Answer: Python writes __iter__ and __next__ for you. A generator gives you the __iter__/__next__ machinery automatically.

What does list(CountDown(4)) produce for the lesson's CountDown iterator?

CountDown(4) yields 4, 3, 2, 1 before raising StopIteration.