繁体   English   中英

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

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

已经搜索了相同的SO,并且也看到了CPython的github存储库; 但无济于事。 似乎看不到任何控制流构造的源代码实现,但不清楚为什么?

特别需要在 CPython 中获取“for - 控制流构造”的源代码。

在不知情的情况下,我所能做的就是在一小段代码上使用dis模块的dis(),导致FOR_ITER操作码,我无法理解。
这个操作码也没有让我了解嵌套的 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

此提交中添加了实现 这是关于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;

忽略引用计数,一个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

考虑当前 CPython 代码库 (3.8.5) 的主题:

您可以在反汇编中看到,每个FOR_ITER前面都有一个GET_ITER

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实际上将for循环遍历的对象iterable传递给PyObject_GetIter

编码:

  1. 使iterable指向python 对象堆栈的顶部;
  2. 使iter指向PyObject_GetIter调用返回的迭代器;
  3. 减少对iterable的引用计数;
  4. 迭代器iter现在位于堆栈顶部。

PyObject_GetIter检查迭代器是否是迭代器(即消耗迭代器的东西),如果是则返回它。 如果不是,则检查它是否是一个序列。 如果是序列,则将其转换为迭代器。 该迭代器是返回值。


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();
    }

感兴趣的部分:

  1. 从栈顶获取迭代器;
  2. 获取next (ie tp_iternext)方法调用的结果;
  3. 如果结果不是NULL则将结果压入堆栈。

您应该问一件事:这仅涵盖循环的一次迭代。 使迭代器遍历所有项目的代码在哪里?

JUMP_ABSOLUTE操作码使迭代器再次运行,这次是在下一个元素上。 您可以在原始清单中看到,每个JUMP_ABSOLUTE都使用相应的FOR_ITER操作码的行号进行FOR_ITER ,从而使迭代成为可能。

这个答案也是关于这个主题的一个很好的参考。

暂无
暂无

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

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