I am novice in Python and programming. Generators are a bit too complicated to understand for new programmers. Here's my theory on generator functions in Python:
Any function contains a
yield
statement will return a generator objectA generator object is a stack contains state
Each time I call
.next
method Python extracts the function's state and when it finds another yield statement it'll bind the state again and deletes the prior state:
Example:
[
[state1] # Stack contains states and states contain info about the function
[state2] # State1 will be deleted when python finds the other yield?
]
This is of course might be like the stupidest theory on earth, but forgive me I am just new in the coding word.
My Questions:
What Python internally makes to store the states ?
Does
yield
statement adds a state to a stack if it exists ?What yield creates internally ? I understand yield creates a generator object, however, I wonder what generator objects contain that makes them work ? are they just a stack/list of states and we you use
.next
method to extract each state and Python will call the function with the indexed state automatically for instance ?
This is correct. The return value of a function containing a
yield
is a generator object. The generator object is an iterator, where each iteration returns a value that wasyield
ed from the code backing the generator.A generator object contains a pointer to a the current execution frame, along with a whole bunch of other stuff used to maintain the state of the generator. The execution frame is what contains the call stack for the code in the generator.
Sort of. When you call
next(gen_object)
, Python evaluates the current execution frame:PyEval_EvalFrame
is highest-level function used to interpret Python bytecode:It knows that when it hits a
yield
while evaluating the bytecode, it should return the value being yielded to the caller:When you yield, the current value of the frame's value stack is maintained (via
f->f_stacktop = stack_pointer
), so that we can resume where we left off whennext
is called again. All non-generator functions setf_stacktop
toNULL
after they're done evaluating. So when you callnext
again on the generator object,PyEval_ExvalFrameEx
is called again, using the same frame pointer as before. The pointer's state will be exactly the same as it was when it yielded during the previous, so execution will continue on from that point. Essentially the current state of the frame is "frozen". This is described in the PEP that introduced generators:Here is most of the state a generator object maintains (taken directly from its header file):
gi_frame
is the pointer to the current execution frame.Note that all of this is CPython implementation-specific. PyPy/Jython/etc. could very well be implementing generators in a completely different way. I encourage you to read through the source for generator objects to learn more about CPython's implementation.