Question presented to candidate:
"Generators and coroutines both pause and resume, and they even share methods like send(). So what really is the difference between a generator and a coroutine in Python, and why can you not just await a generator?"
What a strong answer should cover:
- Purpose: a generator (
def+yield) produces a sequence of values for its caller; a native coroutine (async def) waits for results and is driven by an event loop. - Who drives it: a generator is pulled with
next()/for; a coroutine is resumed withsend(None)by an event loop and is not an iterator (next(coro)is aTypeError). - The shared machinery: both are suspended frames with
send/throw/close, both report their return value throughStopIteration.value; the code-object flags differ (GENERATORvsCOROUTINE), andinspectcan tell them apart. - Awaitability: only coroutines, Futures/Tasks, objects with
__await__, andtypes.coroutine-decorated generators can be awaited;awaiton a bare generator is aTypeError. - History: generator-based coroutines (PEP 342
send, PEP 380yield from,@asyncio.coroutine) led toasync/awaitin PEP 492 (3.5);@asyncio.coroutineis gone in 3.11. - The bottom of every
awaitchain is ayieldto the event loop β "a coroutine is a generator with a different job and a stricter type".
Code / implementation expected: Optional β showing next() on a generator vs send(None) on a coroutine, or a toy event loop.