生成器如何在python中工作 [英] how generators work in python

查看:93
本文介绍了生成器如何在python中工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是Python和编程的新手.对于新程序员来说,生成器有点太复杂了,难以理解.这是我关于Python生成器函数的理论:

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:

  1. 任何包含yield语句的函数都将返回生成器对象

  1. Any function contains a yield statement will return a generator object

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

A generator object is a stack contains state

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

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:

示例:

 [ 
  [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.

我的问题:

  1. Python内部用来存储状态的是什么?

  1. What Python internally makes to store the states ?

yield语句是否将状态添加到堆栈(如果存在)?

Does yield statement adds a state to a stack if it exists ?

内部产生什么收益?我知道yield会创建一个生成器对象,但是,我想知道包含哪些生成器对象才能使它们工作?它们只是状态的堆栈/列表,我们使用.next方法提取每个状态,例如,Python会自动使用索引状态调用该函数?

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 ?

推荐答案

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

Any function contains a yield statement will return a generator object

这是正确的.包含yield的函数的返回值是生成器对象.生成器对象是一个迭代器,其中每次迭代都返回一个值,该值是从支持生成器的代码中yield得出的.

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 was yielded from the code backing the generator.

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

A generator object is a stack contains state

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

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.

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

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

排序.调用next(gen_object)时,Python 评估当前执行框架:

Sort of. When you call 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是最高级别的函数用于解释Python字节码:

PyObject * PyEval_EvalFrameEx(PyFrameObject * f,int throwflag)

这是Python解释的主要,不变的函数.它是 字面长度为2000行.与 执行帧f被执行,解释字节码并执行 根据需要致电.额外的throwflag参数通常可以是 被忽略-如果为true,则导致立即引发异常 抛出用于生成器对象的throw()方法.

This is the main, unvarnished function of Python interpretation. It is literally 2000 lines long. The code object associated with the execution frame f is executed, interpreting bytecode and executing calls as needed. The additional throwflag parameter can mostly be ignored - if true, then it causes an exception to immediately be thrown; this is used for the throw() methods of generator objects.

它知道在评估字节码时击中yield时,它应该将产生的值返回给调用方:

It knows that when it hits a yield while evaluating the bytecode, it should 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中进行了描述:

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 when next is called again. All non-generator functions set f_stacktop to NULL after they're done evaluating. So when you call next 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:

如果遇到yield语句,则函数的状态为 冻结,并将[yielded]的值返回到.next()的 呼叫者. 冻结"是指保留所有本地状态, 包括当前局部变量的绑定,指令 指针和内部评估堆栈:足够的信息是 保存,以便下次调用.next()时,该函数可以 完全像yield语句只是另一个外部语句一样进行 打电话.

If a yield statement is encountered, the state of the function is frozen, and the value [yielded] is returned to .next()'s caller. By "frozen" we mean that all local state is retained, including the current bindings of local variables, the instruction pointer, and the internal evaluation stack: enough information is saved so that the next time .next() is invoked, the function can proceed exactly as if the yield statement were just another external call.

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

Here is most of the state a generator object maintains (taken directly from its header file):

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是指向当前执行帧的指针.

gi_frame is the pointer to the current execution frame.

请注意,所有这些都是CPython实现特定的. PyPy/Jython/等很可能以完全不同的方式实现生成器.我鼓励您通读生成器对象的源代码以进行学习有关CPython实现的更多信息.

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.

这篇关于生成器如何在python中工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆