簡體   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