Iteration as an Interface · practice
Pause Work with yield
Lesson 3 needed twenty lines and two classes to walk a
yield instead of
What yield does
A function containing yield is a generator function. Calling it does not run the body:
It returns a generator __iter__ and __next__, it can be passed to for, list, sum, and it is used up after one pass.

The body runs only when something asks for an item, and then it runs until the next yield and stops there:
Read that output carefully. “starting” does not print until the first next(). “between” prints during the second. The function is not running and then returning; it is pausing, keeping everything it had, and resuming where it left off.
When the function ends, the generator raises StopIteration on its own. You never write that.
What happens when you call a generator function?
Local state, kept for free
The reason this replaces the Lesson 3 machinery is that the pause keeps your self._index is now just a place in the code:
total survives across pauses without being an attribute of anything. Compare that with writing RunningTotalIterator with __init__, __iter__, __next__, a stored
Iterating without a list underneath
A generator does not need a collection to walk. It can produce values that never all exist:
There is no list of labels anywhere. Each one is built when asked for and discarded after use, which is the “things too big to hold” idea from Lesson 1, now something you can write.
Making a class iterable with yield
__iter__ may be a generator function, which is the tidiest way to iterate something that is not just a stored list:
Still re-__iter__ starts a new generator with its own position. When __iter__ needs to filter, transform, or flatten, this is what to write. When it just hands over a stored collection, return iter(self._questions) from Lesson 4 is shorter and says so.
One thing to watch
A generator is used up like any iterator, and the mistake is easy to make:
The second sum gets nothing. If you need the values twice, build a list once and reuse it. Lesson 6 is about deciding which result shape you want, and how to stop a lazy source at an exact boundary.
Task
Implement one generator: passing(records, mark) yields each record whose percentage is at least mark, in input order.
Calling passing must not inspect a record yet. It should start only when the caller asks for a result, and it must stay lazy enough that a caller can take one passing record from an endless input.
The supplied running_total and numbered QuestionBank.__iter__ demonstrates a re-