Generators and Coroutines
Generators and coroutines are implemented using specialized frame objects that can suspend and resume execution.Generators
Generators in CPython are implemented with thePyGenObject struct, which consists of an embedded frame and metadata.
Generator Structure
Defined as_PyGenObject_HEAD in Include/internal/pycore_interpframe_structs.h:
Frame Embedding
The frame is embedded in the generator object:- Allocated as a single memory block with the generator
- Can be accessed bidirectionally:
- Generator → Frame: Direct struct member
- Frame → Generator:
_PyGen_GetGeneratorFromFrame()in pycore_genobject.h
Generator Lifecycle
Creation
Generator functions compile to bytecode that starts withRETURN_GENERATOR:
RETURN_GENERATOR executes:
- Create
PyGenObjectwith embedded frame - Copy current frame state to embedded frame
- Set
ownerfield to indicate generator ownership - Push generator object to stack
- Return to caller (destroying current frame)
Execution
When.send() is called on a generator:
gen_send_ex2()in Objects/genobject.c is invoked- Generator’s frame is pushed onto call stack
_PyEval_EvalFrame()resumes execution- Execution continues from last yield point
Yielding
TheYIELD_VALUE instruction:
- Puts value on stack for caller
- Updates frame’s instruction pointer
- Saves interpreter exception state to generator
- Returns execution to calling frame
- Leaves generator frame ready to resume
Destruction
Ingen_dealloc() (Objects/genobject.c):
- Check if frame is exposed as
PyFrameObject - If exposed and has
refcount > 1, calltake_ownership() take_ownership()copies frame to the frame object- Otherwise, clear frame and deallocate generator
Iteration
FOR_ITER Instruction
TheFOR_ITER instruction calls __next__() on the iterator:
FOR_ITER_GEN Specialization
The specializedFOR_ITER_GEN instruction:
- Detects when iterating over a generator
- Bypasses
__next__()call overhead - Directly pushes generator frame and resumes execution
- Significantly faster than generic
FOR_ITER
Chained Generators (yield from)
Theyield from expression efficiently chains generators:
SEND Instruction
Implementsyield from logic:
- Push value onto chained generator’s stack
- Set exception state on generator’s frame
- Resume chained generator execution
- On return, yield value up the chain with
YIELD_VALUE
Loop Structure
CLEANUP_THROW Instruction
Handles exceptions in the send-yield loop:StopIteration: Extractvaluefield, return from generator- Other exceptions: Re-raise
Coroutines
Coroutines are generators that can receive values via.send():
Send Value Flow
Data flows bidirectionally:- Generator → Caller: Value passed to
yieldexpression - Caller → Generator: Argument to
.send()call
Implementation
Both generators and coroutines use the same mechanism:__next__()simply callsself.send(None)send()is implemented ingen_send_ex2()(Objects/genobject.c)- Send argument becomes the value of the
yieldexpression
Yield From with Send
TheSEND instruction passes the send argument down the generator chain:
Coroutine Types
CPython has three coroutine-like types:Generator-based Coroutines
Created with@types.coroutine or asyncio.coroutine:
Native Coroutines
Defined withasync def:
Asynchronous Generators
Combineasync def with yield:
Generator State
Generators track their execution state:State Values
GEN_CREATED- Just created, not startedGEN_RUNNING- Currently executingGEN_SUSPENDED- Yielded, can be resumedGEN_CLOSED- Finished or closed
State Transitions
Detecting Reentrancy
Generator Methods
.send(value)
Resume with a value:.throw(exc)
Inject exception at yield point:.close()
Terminate generator:Example: Generator Inspection
Performance Characteristics
Memory Efficiency
Generators use less memory than lists:Execution Overhead
Per-iteration overhead:- List iteration: ~50 ns/iteration
- Generator iteration: ~100 ns/iteration
- Specialized FOR_ITER_GEN: ~75 ns/iteration
Related Topics
- Frames - Frame structure and lifecycle
- Bytecode Interpreter - How generator bytecode executes
- Code Objects - Generator code compilation
- Exception Handling - Generator exception propagation
