How does for work?
The same for loop walks lists, strings, dictionaries, files, ranges and many more. How? Python asks each of them for an iterator and then pulls values one at a time until there are none left. Once you know that protocol, you can make your own things loopable — and with generators it takes only a yield.
This matters for real work. A log file with ten million lines doesn't fit comfortably in memory as a list, but a loop that reads one line at a time handles it easily. That's lazy evaluation: compute each value only when it's needed.
The iteration protocol
An iterable is anything iter() can turn into an iterator: lists, strings, dicts, sets, files. An iterator hands out the next value each time you call next() on it, and raises StopIteration when it's exhausted. A for loop is just that, automated: call iter() once, then next() until StopIteration, which the loop catches for you.
it = iter(["momo", "chiya"]) print(next(it)) print(next(it)) print(next(it, "nothing left"))
Iterators are one-way and single-use: once exhausted, they stay empty. That's why a zip() or map() object can only be looped over once, while the list you made it from can be looped over again and again.