Iterators and generators
An iterable is anything for can walk. iter(x) gets an iterator from it, and next(it) returns one item at a time, raising StopIteration at the end. An iterator is used up once.
it = iter([1, 2])
next(it) # 1
next(it) # 2
# next(it) would raise StopIterationA class joins in by defining __iter__. If __iter__ builds a fresh iterator each time, the object can be looped over repeatedly.
Generators
A function containing yield returns a generator, which is an iterator that pauses at each yield. Values are made on demand, so sequences can be huge or endless.
def evens():
n = 0
while True:
yield n
n += 2
def both(a, b):
yield from a
yield from byield from hands over to another iterable, which is ideal for recursive walks.
Your task
Write a class Countdown(start) whose instances can be looped over more than once, producing start down to 1. Write a generator function flatten(items) yielding every non-list value from arbitrarily nested lists.
The explanation opens here once every check passes.