Chapter 11 · practice
Iteration as an Interface
Iterable and Iterator
You have written hundreds of for
for question in questions:
print(question.prompt)
Python does not walk questions by questions for an
Two different things
An
An iterator is the thing that does the walking. It holds the position. It knows what comes next.
They are usually not the same
iter() asks an iterable for an iterator. A list is not its own iterator; it hands out a new one each time it is asked. An iterator instead returns itself, keeping the same position. It is therefore iterable too:
That is the design, and it has a consequence you can see immediately.

An iterator is used up
The first list(walker) collected all three. The second collected nothing. The iterator did not rewind, because it has no way back: all it holds is a position, and the position is now past the end.
The list itself is untouched:
Both give three items, because each list() call asked questions for a new iterator starting at the beginning.
Why does looping over the same list twice work, when looping over the same iterator twice does not?
Where this bites
This is not trivia. It causes a specific bug that looks like data disappearing:
Same numbers, two different answers. Given a list, sum() gets an iterator, exhausts it, and then list(scores) asks the list for a fresh one: three items. Given an iterator, sum() exhausts the only one there is, and list(scores) finds nothing left.
The
Why have the split at all
It would be simpler if a list just walked itself. The split buys two things.
Several loops at once. Two nested loops over the same list each get their own position, so the inner loop finishing does not end the outer one.
Nine, not three. With a single shared position that could not work.
Things too big to hold. An iterator only has to know how to produce the next item. It does not need them all to exist, which is what lets a program read a file larger than memory a line at a time. Lesson 6 returns to this.
The next lesson drives the conversation by hand, so the protocol stops being a description and becomes something you have used.
Task
Write three small
walks_twice(items)returns aof two : everything collected on a first pass, and everything collected on a second. A list gives two full lists; an iterator gives one full and one empty. is_own_iterator(items)reports whether asking thefor an iterator hands back the object itself. That is true of an iterator and false of a list. safe_count(items)returns how many items there are, and must work for both. Nothing that iterates its input twice can do this.
Run the program to see each answer for a list and for an iterator over the same three values.