¿Cómo se implementan los generadores y las corutinas en CPython?

He leído que en CPython, la stack de intérpretes (la lista de funciones de Python llamadas para llegar a este punto) se mezcla con la stack de C (la lista de funciones de C que fueron llamadas en el propio código del intérprete). Si es así, ¿cómo se implementan los generadores y las coroutinas? ¿Cómo recuerdan su estado de ejecución? ¿Copia CPython la stack de cada generador / coroutine hacia y desde una stack de sistema operativo? ¿O CPython simplemente mantiene el marco de stack superior del generador en el montón, ya que el generador solo puede ceder desde ese marco superior?

La instrucción de yield toma el contexto de ejecución actual como un cierre y lo transforma en un objeto viviente propio. Este objeto tiene un método __iter__ que continuará después de esta statement de rendimiento.

Así que la stack de llamadas se transforma en un objeto de stack.

La idea de que la stack de Python y la stack de C en un progtwig Python en ejecución se entremezclan puede ser engañosa.

La stack de Python es algo completamente separado de la stack de C real utilizada por el intérprete. Las estructuras de datos en la stack de Python son, en realidad, objetos de “marco” de Python completos (que incluso se pueden analizar en forma introspectiva y se han modificado algunos atributos en el tiempo de ejecución). Esta stack es administrada por la máquina virtual de Python, que se ejecuta en C y, por lo tanto, tiene un progtwig C normal, nivel de máquina, stack.

Cuando se usan generadores e iteradores, el intérprete simplemente almacena el objeto de marco respectivo en otro lugar que no sea en la stack del progtwig Python, y lo empuja hacia atrás cuando se reanuda la ejecución del generador. Este “en otro lugar” es el objeto generador en sí. Al llamar al método “siguiente” o “enviar” en el objeto generador, esto sucede.