How do generators and the yield keyword work in Python?
A generator in Python is a special type of iterator. Unlike a regular function that computes its entire result and returns it, a generator produces values one at a time and suspends execution between each value. This lazy evaluation model enables processing of large or infinite sequences without materializing them in memory.
A generator function is defined like a regular function but contains one or more yield statements. When you call a generator function, it does not execute the function body -- it returns a generator object immediately. The body only starts running when you call next() on the generator. Execution proceeds until the next yield statement, at which point the yielded value is returned to the caller and the function's execution state -- local variables, instruction pointer, everything -- is suspended. The next call to next() resumes exactly where it left off. When the function body exhausts, a StopIteration exception is raised, signaling the iterator is exhausted.
Memory efficiency is the primary benefit. A generator that yields one million numbers uses O(1) memory -- it holds only the current execution frame. A list of one million numbers uses O(n) memory. For pipelines processing large datasets -- reading lines from a file, transforming records -- chaining generators keeps the entire pipeline at O(1) memory overhead.
Generator expressions provide a compact syntax analogous to list comprehensions: (x * 2 for x in range(10)) returns a generator rather than a list.
yield from is a delegation mechanism introduced in Python 3.3. It allows a generator to delegate to another iterable or generator, flattening the delegation without manually iterating in a loop. It also properly forwards send() values and exceptions, which matters for coroutine-style code.
Generators also support the send() method, which allows the caller to send a value back into the generator, making generators a building block for coroutines. Python's asyncio async/await syntax is built on generators at the lower level. Understanding generators is prerequisite to understanding how the event loop and coroutines work internally.
Candidate explains that yield suspends execution and produces values lazily, can write a simple generator, and understands memory efficiency -- but does not know send(), yield from, or the relationship to coroutines and asyncio.
Candidate explains lazy evaluation and execution state preservation precisely, contrasts memory usage with lists, explains yield from and its use in delegation, mentions send() and the generator-as-coroutine pattern, and connects generators to asyncio internals.
Reading the answer is step one. Explaining it unprompted — under interview pressure — is what actually matters. Get AI-graded feedback on your answer with follow-up probes on your weak points.
Get Graded — Free Assessment