Internally, a generator works about the same as a regular function call. Under-the-hood, running generators and running functions use mostly the same machinery.
When you call either a function or a generator, a stackframe is created. It has the local variables (including the arguments passed into the function), a code pointer to the active opcode, and a stack for pending try-blocks, with-blocks, or loops.
In a regular function, execution begins immediately. When return
is encountered, the final result is kept and the stackframe is freed along with everything it referenced.
In a generator function, the stackframe is wrapped in a generator-iterator object and returned immediately. The code in the generator function only runs when called by next(g)
or g.send(v)
. Execution is suspended when yield
is encountered.
One way to think of generators is that they are like functions that can be paused with yield
and resumed with g.next()
. The stackframe is kept alive, so resuming a running generator is much cheaper than making a new function call which has to build a new frame on every invocation.