我是Python和编程的新手。对于新程序员来说,生成器有点太复杂了。这是我在Python中关于生成器函数的理论:
任何函数都包含yield
语句将返回生成器对象
生成器对象是包含状态的堆栈
每次调用.next
方法时,Python都会提取函数的状态,当它找到另一个yield语句时,它会再次绑定状态并删除先前的状态:
示例:
[
[state1] # Stack contains states and states contain info about the function
[state2] # State1 will be deleted when python finds the other yield?
]
这当然可能就像地球上最愚蠢的理论一样,但请原谅我,我只是编码中的新词。
我的问题:
Python内部用于存储状态的内容是什么?
yield
语句是否会向堆栈添加状态?
内部产生什么收益?我理解yield会创建一个生成器对象,但是,我想知道生成器对象包含什么使它们工作?它们只是一个堆栈/状态列表,我们使用.next
方法提取每个状态,Python会自动调用具有索引状态的函数吗?
答案 0 :(得分:15)
任何函数都包含一个yield语句将返回一个生成器对象
这是对的。包含yield
的函数的返回值是生成器对象。生成器对象是一个迭代器,每次迭代都会从支持生成器的代码返回yield
的值。
生成器对象是包含状态的堆栈
生成器对象包含指向当前执行帧的指针,以及用于维护生成器状态的一大堆其他内容。执行帧包含生成器中代码的调用堆栈。
每次调用.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参数大多数可以是 忽略 - 如果为true,则会立即导致异常 抛出;这用于生成器对象的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语句,则函数的状态为 冻结,值[yielding]返回到.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实施的更多信息。