简体   繁体   English

如何在 Python 中找到 for - 控制流构造的实现

[英]How to find implementation of for - control flow construct in Python

Have searched SO for the same, and seen the github repository of CPython too;已经搜索了相同的SO,并且也看到了CPython的github存储库; but of no avail.但无济于事。 It seems that source code implementation of any control flow construct is not visible, but it is not clear why?似乎看不到任何控制流构造的源代码实现,但不清楚为什么?

In particular need get source code for 'for - control flow construct' in CPython.特别需要在 CPython 中获取“for - 控制流构造”的源代码。

In face of no knowledge, all I could do is to use dis module's dis() on a small code, leading to FOR_ITER opcode, which is not understandable by me.在不知情的情况下,我所能做的就是在一小段代码上使用dis模块的dis(),导致FOR_ITER操作码,我无法理解。
Nor does this opcode lead me into understanding the nested for-loop construct's workings, the reason why I wanted to look into implementation of the same in source code.这个操作码也没有让我了解嵌套的 for 循环结构的工作原理,这也是我想研究源代码中相同结构的实现的原因。

>import dis
 def foo():
 for i in range(3):
     for j in range(2):
         print(i,j)
 dis.dis(foo)

 3           0 SETUP_LOOP              44 (to 46)
             2 LOAD_GLOBAL              0 (range)
             4 LOAD_CONST               1 (3)
             6 CALL_FUNCTION            1
             8 GET_ITER
       >>   10 FOR_ITER                32 (to 44)
            12 STORE_FAST               0 (i)

 4          14 SETUP_LOOP              26 (to 42)
            16 LOAD_GLOBAL              0 (range)
            18 LOAD_CONST               2 (2)
            20 CALL_FUNCTION            1
            22 GET_ITER
       >>   24 FOR_ITER                14 (to 40)
            26 STORE_FAST               1 (j)

 5          28 LOAD_GLOBAL              1 (print)
            30 LOAD_FAST                0 (i)
            32 LOAD_FAST                1 (j)
            34 CALL_FUNCTION            2
            36 POP_TOP
            38 JUMP_ABSOLUTE           24
       >>   40 POP_BLOCK
       >>   42 JUMP_ABSOLUTE           10
       >>   44 POP_BLOCK
       >>   46 LOAD_CONST               0 (None)
            48 RETURN_VALUE

The implementation was added in this commit ;此提交中添加了实现 here's the part about FOR_ITER :这是关于FOR_ITER的部分:

        case FOR_ITER:
            /* before: [iter]; after: [iter, iter()] *or* [] */
            v = TOP();
            x = PyObject_CallObject(v, NULL);
            if (x == NULL) {
                if (PyErr_ExceptionMatches(
                    PyExc_StopIteration))
                {
                    PyErr_Clear();
                    x = v = POP();
                    Py_DECREF(v);
                    JUMPBY(oparg);
                    continue;
                }
                break;
            }
            PUSH(x);
            continue;

Ignoring the refcounting, a for x in y: loop is equivalent to the following Python code:忽略引用计数,一个for x in y:循环等效于以下 Python 代码:

# GET_ITER
y_iter = iter(y)

# FOR_ITER
while True:
    try:
        x = next(y_iter)
    except StopIteration:
        break

    # body of for loop
    pass

Considering the subject on current CPython code base (3.8.5):考虑当前 CPython 代码库 (3.8.5) 的主题:

You can see in your disassembly that every FOR_ITER is preceded by a GET_ITER .您可以在反汇编中看到,每个FOR_ITER前面都有一个GET_ITER

GET_ITER source code (check the numbered comments): GET_ITER源代码(检查编号注释):

    case TARGET(GET_ITER): {
        /* before: [obj]; after [getiter(obj)] */
        PyObject *iterable = TOP(); // 1.
        PyObject *iter = PyObject_GetIter(iterable); // 2.
        Py_DECREF(iterable); // 3.
        SET_TOP(iter); // 4.
        if (iter == NULL)
            goto error;
        PREDICT(FOR_ITER);
        PREDICT(CALL_FUNCTION);
        DISPATCH();
    }

GET_ITER actually passes to PyObject_GetIter the object iterable which is traversed by for loop. GET_ITER实际上将for循环遍历的对象iterable传递给PyObject_GetIter

The code:编码:

  1. Makes iterable points to top of stack of python objects ;使iterable指向python 对象堆栈的顶部;
  2. Makes iter points to the iterator returned by PyObject_GetIter call;使iter指向PyObject_GetIter调用返回的迭代器;
  3. Decreases the reference count to the iterable ;减少对iterable的引用计数;
  4. Iterator iter is now on top of stack.迭代器iter现在位于堆栈顶部。

PyObject_GetIter checks if the iterable is an iterator (ie something that consumes iterables) and if so returns it. PyObject_GetIter检查迭代器是否是迭代器(即消耗迭代器的东西),如果是则返回它。 If it's not, then checks if it's a sequence.如果不是,则检查它是否是一个序列。 If it is a sequence, then it's converted to an iterator.如果是序列,则将其转换为迭代器。 That iterator is the returned value.该迭代器是返回值。


FOR_ITER code: FOR_ITER代码:

    case TARGET(FOR_ITER): {
        PREDICTED(FOR_ITER);
        /* before: [iter]; after: [iter, iter()] *or* [] */
        PyObject *iter = TOP(); // 1.
        PyObject *next = (*iter->ob_type->tp_iternext)(iter); // 2.
        if (next != NULL) {
            PUSH(next); // 3.
            PREDICT(STORE_FAST);
            PREDICT(UNPACK_SEQUENCE);
            DISPATCH();
        }
        if (_PyErr_Occurred(tstate)) {
            if (!_PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) {
                goto error;
            }
            else if (tstate->c_tracefunc != NULL) {
                call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, f);
            }
            _PyErr_Clear(tstate);
        }
        /* iterator ended normally */
        STACK_SHRINK(1);
        Py_DECREF(iter);
        JUMPBY(oparg);
        PREDICT(POP_BLOCK);
        DISPATCH();
    }

Parts of interest:感兴趣的部分:

  1. Gets the iterator from top of stack;从栈顶获取迭代器;
  2. Gets the result of next (ie tp_iternext) method call;获取next (ie tp_iternext)方法调用的结果;
  3. Pushes the result into the stack if it's not NULL .如果结果不是NULL则将结果压入堆栈。

One thing you should be asking: this only covers a single iteration of the loop.您应该问一件事:这仅涵盖循环的一次迭代。 Where is the code that makes the iterator traverse all the items ?使迭代器遍历所有项目的代码在哪里?

It's the JUMP_ABSOLUTE opcode that makes the iterator run again, this time on the next element.JUMP_ABSOLUTE操作码使迭代器再次运行,这次是在下一个元素上。 You can see in your original listing that each JUMP_ABSOLUTE is called with the line number of the corresponding FOR_ITER opcode, making the iteration possible.您可以在原始清单中看到,每个JUMP_ABSOLUTE都使用相应的FOR_ITER操作码的行号进行FOR_ITER ,从而使迭代成为可能。

This answer is a good reference on this subject too. 这个答案也是关于这个主题的一个很好的参考。

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

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