Below is a generator function.
y = yield x
x += y
self.x = 1
def send(self, y):
self.x += y
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
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.