首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >发电机在python中的工作方式

发电机在python中的工作方式
EN

Stack Overflow用户
提问于 2014-08-10 19:43:26
回答 1查看 2K关注 0票数 10

我是Python和编程方面的新手。对于新的程序员来说,生成器太复杂了,无法理解。下面是我对Python中生成器函数的理论:

  1. 任何包含yield语句的函数都将返回生成器对象。
  2. 生成器对象是包含状态的堆栈。
  3. 每次我调用.next方法时,Python都提取函数的状态,当它找到另一个deletes语句时,它将再次绑定该状态并删除先前的状态:

示例:

代码语言:javascript
运行
复制
 [ 
  [state1] # Stack contains states and states contain info about the function
  [state2] # State1 will be deleted when python finds the other yield? 
 ] 

当然,这可能是地球上最愚蠢的理论,但请原谅我,我只是一个新的编码词。

我的问题:

  1. Python内部做了什么来存储状态呢?
  2. 如果存在yield语句是否将状态添加到堆栈中?
  3. 什么产量在内部产生?我知道产创建一个生成器对象,但是,我想知道生成器对象包含哪些使它们工作的内容?它们仅仅是一个堆栈/状态列表吗?我们使用.next方法提取每个状态,例如,Python会用索引状态自动调用函数吗?
EN

回答 1

Stack Overflow用户

发布于 2014-08-10 21:14:45

任何包含产额语句的函数都将返回生成器对象。

这是正确的。包含yield的函数的返回值是生成器对象。生成器对象是一个迭代器,其中每次迭代都返回支持生成器的代码中的yield编辑的值。

生成器对象是包含状态的堆栈。

生成器对象包含指向当前执行框架的指针,以及用于维护生成器状态的大量其他东西。执行框架包含生成器中代码的调用堆栈。

每次我调用.next方法时,Python都会提取函数的状态,当它找到另一条收益率语句时,它会再次绑定状态并删除先前的状态

说大也大吧。当您调用next(gen_object)时,Python 评估当前执行框架。

代码语言:javascript
运行
复制
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是最高级别的函数用于解释Python字节码

PyObject PyEval_EvalFrameEx(PyFrameObject f,int行标志) 这是Python解释的主要、未修饰的函数。从字面上说,它有2000行长。执行与执行框架f相关联的代码对象,解释字节码并根据需要执行调用。附加的throw频标志参数可以被忽略-如果为真,则会立即抛出异常;这用于生成器对象的抛出()方法。

它知道当它在计算字节码时碰到yield时,它应该是返回被释放给调用方的值。

代码语言:javascript
运行
复制
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中有描述。

如果遇到屈服语句,则会冻结函数的状态,并将得到的值返回给.next()的调用方。所谓“冻结”,指的是保留所有本地状态,包括当前局部变量绑定、指令指针和内部计算堆栈:保存了足够的信息,以便在下次调用.next()时,函数完全可以继续进行,就好像产量语句只是另一个外部调用一样。

下面是生成器对象维护的大部分状态(直接从其头文件中获取):

代码语言:javascript
运行
复制
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/等很可能以完全不同的方式实现生成器。我鼓励您通过读取生成器对象的源了解更多关于CPython实现的信息。

票数 20
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/25232350

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档