0%

Iteration as an Interface · practice

Understand the Iterator Protocol

iter() and next() are the caller’s side. This lesson is the other side: what an has to provide to be walked at all.

It is two , and you have met the naming convention for both.

The two methods

__iter__ returns an . Python calls it when something asks for one, which iter() and every for do.

__next__ returns the next item, or raises StopIteration when there are none left. Python calls it when something asks for an item, which next() does.

An defines __iter__. An iterator defines both, and its __iter__ returns itself.

Here is the smallest complete example:

Try it

No anywhere. The numbers do not exist until __next__ produces them, and the for loop neither knows nor cares.

Reading it slowly

__iter__ returns self, which is the definition of an iterator: it is the walker, so asking it for one hands back itself. That is the same iter(x) is x you tested in Lesson 1.

__next__ does the work. It checks whether there is anything left, raises StopIteration if not, moves the position, and returns the item. Every iterator ever written has that shape.

Notice what happens on a second pass:

Try it

Exhausted, exactly like the list iterator in Lesson 1. Countdown holds its position in self.current, and nothing resets it. It is an iterator, so it is single use.

What must __next__ do when there is nothing left?

Iterable and iterator as separate objects

Countdown is both at once, which is why it is used up. When an object should be re-iterable, keep the two apart: the iterable makes a new iterator each time.

Try it

Both passes see both items, because each list() call got a fresh BankWalker with its own _index. The bank holds the data; the walker holds the position. That is exactly the arrangement a list has with its own iterator, and now you can see why.

Do not write this

Two classes and about twenty lines to walk a list you already have. In real code, almost never.

This lesson exists so the protocol is concrete rather than a description, and the next two lessons show what you actually write instead: __iter__ that delegates to something already iterable, and generators, which produce this whole shape from an ordinary .

The exercise builds one full iterator by hand, once.

Task

Write the protocol by hand, once, so it stops being a description.

Countdown(start) is an that yields start, start - 1, down to 1, and then stops. It defines both : __iter__ returns itself, and __next__ produces the next number or raises StopIteration. A Countdown(0) produces nothing at all.

ScoreLog(scores) is an , not an iterator. Its __iter__ returns a new LogWalker each time it is asked, so the same log can be walked twice.

LogWalker(scores) is the iterator: it holds the position, returns itself from __iter__, and raises StopIteration once it passes the end.

The grader walks a ScoreLog twice and expects both passes to see everything, and walks a Countdown twice and expects the second to be empty. That difference is the whole point.