Iterators, Generators and Context Managers
Know the difference between iterables and single-pass iterators, how generators pause and resume (including send, throw and close), and how the with statement and contextlib guarantee cleanup.
Key points
- 1
An iterable produces iterators via
__iter__; an iterator also has__next__and is used up after one pass. map, zip, filter, generators and files are iterators; lists, ranges and dict views are not. - 2
Calling a generator function runs nothing.
next()runs to the nextyield;send(v)makes the pausedyieldevaluate tov(prime withnext()first);throw()raises at theyield;close()raises GeneratorExit there. - 3
A generator's
return valuebecomesStopIteration.value, andyield fromevaluates to it.yield fromalso forwards send, throw and close to the subgenerator. - 4
In a generator expression only the outermost iterable is evaluated at creation; conditions and inner loops run lazily and see later rebindings.
- 5
withcalls__enter__(its return is bound byas) and always calls__exit__; a truthy return from__exit__suppresses the exception. Multiple managers exit in reverse order. - 6
With
@contextmanager, the body's exception is raised at theyield: wrap it in try/finally for cleanup, and yield exactly once. - 7
Since PEP 479, a StopIteration leaking out of a generator body becomes RuntimeError; use
next(it, default)instead.
Common traps
x in gen,zip(it, it)andtakewhileall consume items you may expect to still be there.Yielding inside
finallyorexcept GeneratorExitmakesclose()raise "generator ignored GeneratorExit".Breaking out of
async fordoes not close the async generator immediately; usecontextlib.aclosing()for deterministic cleanup.
Read the source
Test yourself on Iterators, Generators and Context Managers
Ten questions, with the answer and explanation after each one.