简体   繁体   English

生成器如何在python中工作

[英]how generators work in python

I am novice in Python and programming. 我是Python和编程的新手。 Generators are a bit too complicated to understand for new programmers. 对于新程序员来说,生成器有点太复杂了。 Here's my theory on generator functions in Python: 这是我在Python中关于生成器函数的理论:

  1. Any function contains a yield statement will return a generator object 任何包含yield语句的函数都将返回一个生成器对象

  2. A generator object is a stack contains state 生成器对象是堆栈包含状态

  3. 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方法时,Python都会提取函数的状态,当它找到另一个yield语句时,它会再次绑定状态并删除先前的状态:

Example: 例:

 [ 
  [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. 这当然可能就像地球上最愚蠢的理论,但请原谅我,我只是编码中的新词。

My Questions: 我的问题:

  1. What Python internally makes to store the states ? Python内部用于存储状态的内容是什么?

  2. Does yield statement adds a state to a stack if it exists ? 如果存在, yield语句是否会向堆栈添加状态?

  3. What yield creates internally ? 什么产量在内部产生? I understand yield creates a generator object, however, I wonder what generator objects contain that makes them work ? 我理解yield会创建一个生成器对象,但是,我想知道生成器对象包含什么使它们工作? 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 ? 它们只是一个堆栈/状态列表,我们使用.next方法提取每个状态,Python会自动调用具有索引状态的函数吗?

Any function contains a yield statement will return a generator object 任何包含yield语句的函数都将返回一个生成器对象

This is correct. 这是对的。 The return value of a function containing a yield is a generator object. 包含yield的函数的返回值是生成器对象。 The generator object is an iterator, where each iteration returns a value that was yield ed from the code backing the generator. 生成器对象是一个迭代,其中每次迭代返回是一个值yield编从码背衬发电机。

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. 执行帧包含生成器中代码的调用堆栈。

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方法时,Python都会提取函数的状态,当它找到另一个yield语句时,它会再次绑定状态并删除先前的状态

Sort of. 有点。 When you call next(gen_object) , Python evaluates the current execution frame : 当你调用next(gen_object) ,Python会评估当前的执行帧

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 is highest-level function used to interpret Python bytecode : PyEval_EvalFrame用于解释Python字节码的最高级函数:

PyObject* PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) PyObject * PyEval_EvalFrameEx(PyFrameObject * f,int throwflag)

This is the main, unvarnished function of Python interpretation. 这是Python解释的主要功能。 It is literally 2000 lines long. 它实际上是2000行。 The code object associated with the execution frame f is executed, interpreting bytecode and executing calls as needed. 执行与执行帧f相关联的代码对象,解释字节码并根据需要执行调用。 The additional throwflag parameter can mostly be ignored - if true, then it causes an exception to immediately be thrown; 额外的throwflag参数大多可以忽略 - 如果为true,则会导致立即抛出异常; this is used for the throw() methods of generator objects. 这用于生成器对象的throw()方法。

It knows that when it hits a yield while evaluating the bytecode, it should return the value being yielded to the caller : 它知道当它在评估字节码时达到yield时,它应该返回给调用者的值

    TARGET(YIELD_VALUE) {
        retval = POP();
        f->f_stacktop = stack_pointer;
        why = WHY_YIELD;
        goto fast_yield;
    }

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. 当你屈服时,帧的值栈的当前值被保持(通过f->f_stacktop = stack_pointer ),这样我们可以在再次调用next时从中断处继续。 All non-generator functions set f_stacktop to NULL after they're done evaluating. 所有非生成器函数在完成评估后将f_stacktop设置为NULL So when you call next again on the generator object, PyEval_ExvalFrameEx is called again, using the same frame pointer as before. 因此,当您再次在生成器对象上调用next时,将再次调用PyEval_ExvalFrameEx ,使用与之前相同的帧指针。 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 : 引入生成器PEP中描述了这一点:

If a yield statement is encountered, the state of the function is frozen, and the value [yielded] is returned to .next()'s caller. 如果遇到yield语句,则冻结函数的状态,并将值[yielding]返回给.next()的调用者。 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. “冻结”是指保留所有本地状态,包括局部变量,指令指针和内部评估堆栈的当前绑定:保存足够的信息以便下次调用.next()时,函数可以就像yield语句只是另一个外部调用一样。

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 is the pointer to the current execution frame. gi_frame是指向当前执行帧的指针。

Note that all of this is CPython implementation-specific. 请注意,所有这些都是CPython特定于实现的。 PyPy/Jython/etc. PyPy / Jython的/等。 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. 我鼓励您阅读生成器对象的源代码,以了解有关CPython实现的更多信息。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM