Study notes · 9% of the exam

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. 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. 2

    Calling a generator function runs nothing. next() runs to the next yield; send(v) makes the paused yield evaluate to v (prime with next() first); throw() raises at the yield; close() raises GeneratorExit there.

  3. 3

    A generator's return value becomes StopIteration.value, and yield from evaluates to it. yield from also forwards send, throw and close to the subgenerator.

  4. 4

    In a generator expression only the outermost iterable is evaluated at creation; conditions and inner loops run lazily and see later rebindings.

  5. 5

    with calls __enter__ (its return is bound by as) and always calls __exit__; a truthy return from __exit__ suppresses the exception. Multiple managers exit in reverse order.

  6. 6

    With @contextmanager, the body's exception is raised at the yield: wrap it in try/finally for cleanup, and yield exactly once.

  7. 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) and takewhile all consume items you may expect to still be there.

  • Yielding inside finally or except GeneratorExit makes close() raise "generator ignored GeneratorExit".

  • Breaking out of async for does not close the async generator immediately; use contextlib.aclosing() for deterministic cleanup.

Test yourself on Iterators, Generators and Context Managers

Ten questions, with the answer and explanation after each one.