任何包含yield语句的函数都会返回一个生成器对象
这是正确的。包含yield 的函数的返回值是一个生成器对象。生成器对象是一个迭代器,其中每次迭代都会返回一个值,该值是从支持生成器的代码中yielded。
生成器对象是包含状态的堆栈
生成器对象包含一个指向当前执行帧的指针,以及用于维护生成器状态的一大堆其他内容。执行帧包含生成器中代码的调用堆栈。
每次我调用 .next 方法时,Python 都会提取函数的状态并
当它找到另一个 yield 语句时,它将再次绑定状态并
删除之前的状态
有点。当你调用next(gen_object),Python evaluates the current execution frame:
gen_send_ex(PyGenObject *gen, PyObject *arg, int exc) { // This is called when you call next(gen_object)
PyFrameObject *f = gen->gi_frame;
...
gen->gi_running = 1;
result = PyEval_EvalFrameEx(f, exc); // This evaluates the current frame
gen->gi_running = 0;
PyEval_EvalFrame 是最高级别的函数used to interpret Python bytecode:
PyObject* PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
这是 Python 解释的主要、朴素的功能。它是
字面意思是 2000 行长。与关联的代码对象
执行帧 f 被执行,解释字节码并执行
根据需要调用。附加的 throwflag 参数大多可以是
忽略 - 如果为真,则立即引发异常
抛出; this 用于生成器对象的 throw() 方法。
它知道当它在评估字节码时遇到yield,它应该return the value being yielded to the caller:
TARGET(YIELD_VALUE) {
retval = POP();
f->f_stacktop = stack_pointer;
why = WHY_YIELD;
goto fast_yield;
}
当你让出时,帧的值堆栈的当前值被保持(通过f->f_stacktop = stack_pointer),这样我们可以在再次调用next 时从中断的地方继续。所有非生成器函数在完成评估后将f_stacktop 设置为NULL。因此,当您在生成器对象上再次调用next 时,会再次调用PyEval_ExvalFrameEx,使用与以前相同的帧指针。指针的状态将与前一次产生时完全相同,因此将从该点继续执行。本质上,框架的当前状态是“冻结的”。这在PEP that introduced generators 中有描述:
如果遇到yield语句,函数的状态是
冻结,并将值 [yielded] 返回到 .next() 的
呼叫者。 “冻结”是指保留所有本地状态,
包括当前的局部变量绑定,指令
指针和内部评估堆栈:足够的信息是
保存以便下次调用 .next() 时,该函数可以
就像 yield 语句只是另一个外部语句一样进行
打电话。
这是生成器对象维护的大部分状态(直接取自其头文件):
typedef struct {
PyObject_HEAD
/* The gi_ prefix is intended to remind of generator-iterator. */
/* Note: gi_frame can be NULL if the generator is "finished" */
struct _frame *gi_frame;
/* True if generator is being executed. */
char gi_running;
/* The code object backing the generator */
PyObject *gi_code;
/* List of weak reference. */
PyObject *gi_weakreflist;
/* Name of the generator. */
PyObject *gi_name;
/* Qualified name of the generator. */
PyObject *gi_qualname;
} PyGenObject;
gi_frame是指向当前执行帧的指针。
请注意,所有这些都是特定于 CPython 实现的。 PyPy/Jython/等。很可能以完全不同的方式实现生成器。我鼓励您read through the source for generator objects 了解更多关于 CPython 的实现。