大師兄的Python源碼學(xué)習(xí)筆記(十五): 虛擬機(jī)中的控制流(二)

大師兄的Python源碼學(xué)習(xí)筆記(十四): 虛擬機(jī)中的控制流(一)
大師兄的Python源碼學(xué)習(xí)筆記(十六): 虛擬機(jī)中的控制流(三)

二、虛擬機(jī)中的for循環(huán)控制流

  • 與if控制結(jié)構(gòu)不同,for循環(huán)控制結(jié)構(gòu)包含一種特殊的指令跳躍方式:指令回退
  • 先寫一段簡單的代碼:
demo.py

for i in range(10):
    print(1)
  • 生成的字節(jié)碼指令序列如下:
  1           0 SETUP_LOOP              24 (to 26)
              2 LOAD_NAME                0 (range)
              4 LOAD_CONST               0 (10)
              6 CALL_FUNCTION            1
              8 GET_ITER
        >>   10 FOR_ITER                12 (to 24)
             12 STORE_NAME               1 (i)

  2          14 LOAD_NAME                2 (print)
             16 LOAD_CONST               1 (1)
             18 CALL_FUNCTION            1
             20 POP_TOP
             22 JUMP_ABSOLUTE           10
        >>   24 POP_BLOCK
        >>   26 LOAD_CONST               2 (None)
             28 RETURN_VALUE
1. 循環(huán)控制結(jié)構(gòu)的初始化
  • for循環(huán)控制流的實(shí)現(xiàn)從SETUP_LOOP開始:
ceval.c

        TARGET(SETUP_LOOP)
        TARGET(SETUP_EXCEPT)
        TARGET(SETUP_FINALLY) {
            /* NOTE: If you add any new block-setup opcodes that
               are not try/except/finally handlers, you may need
               to update the PyGen_NeedsFinalizing() function.
               */

            PyFrame_BlockSetup(f, opcode, INSTR_OFFSET() + oparg,
                               STACK_LEVEL());
            DISPATCH();
        }
  • 可以看到,應(yīng)對SETUP_LOOP虛擬機(jī)調(diào)用了PyFrame_BlockSetup。
Objects\frameobject.c

void
PyFrame_BlockSetup(PyFrameObject *f, int type, int handler, int level)
{
    PyTryBlock *b;
    if (f->f_iblock >= CO_MAXBLOCKS)
        Py_FatalError("XXX block stack overflow");
    b = &f->f_blockstack[f->f_iblock++];
    b->b_type = type;
    b->b_level = level;
    b->b_handler = handler;
}
  • PyFrame_BlockSetup使用了PyFrameObject中的f_iblock
Include\frameobject.h

typedef struct _frame {
    PyObject_VAR_HEAD
    struct _frame *f_back;      /* previous frame, or NULL */
    PyCodeObject *f_code;       /* code segment */
    PyObject *f_builtins;       /* builtin symbol table (PyDictObject) */
    PyObject *f_globals;        /* global symbol table (PyDictObject) */
    PyObject *f_locals;         /* local symbol table (any mapping) */
    PyObject **f_valuestack;    /* points after the last local */
    /* Next free slot in f_valuestack.  Frame creation sets to f_valuestack.
       Frame evaluation usually NULLs it, but a frame that yields sets it
       to the current stack top. */
    PyObject **f_stacktop;
    PyObject *f_trace;          /* Trace function */
    char f_trace_lines;         /* Emit per-line trace events? */
    char f_trace_opcodes;       /* Emit per-opcode trace events? */

    /* Borrowed reference to a generator, or NULL */
    PyObject *f_gen;

    int f_lasti;                /* Last instruction if called */
    /* Call PyFrame_GetLineNumber() instead of reading this field
       directly.  As of 2.3 f_lineno is only valid when tracing is
       active (i.e. when f_trace is set).  At other times we use
       PyCode_Addr2Line to calculate the line from the current
       bytecode index. */
    int f_lineno;               /* Current line number */
    int f_iblock;               /* index in f_blockstack */
    char f_executing;           /* whether the frame is still executing */
    PyTryBlock f_blockstack[CO_MAXBLOCKS]; /* for try and loop blocks */
    PyObject *f_localsplus[1];  /* locals+stack, dynamically sized */
} PyFrameObject;
  • CO_MAXBLOCK是一個(gè)值為20的常量,用來限制靜態(tài)嵌套塊的數(shù)量。
Include\frameobject.h

#define CO_MAXBLOCKS 20 /* Max static block nesting within a function */
  • f_blockstack是一個(gè)PyTryBlock結(jié)構(gòu),結(jié)構(gòu)中存放了PyTryBlock的類型、當(dāng)前執(zhí)行的字節(jié)碼指令、棧的深度等信息,用于循環(huán)控制流。
Include\frameobject.h

typedef struct {
    int b_type;                 /* what kind of block this is */
    int b_handler;              /* where to jump to find handler */
    int b_level;                /* value stack level to pop to */
} PyTryBlock;
2. list迭代器
  • 案例中,在申請PyTryBlock結(jié)構(gòu)的空間后,虛擬機(jī)通過LOAD_NAME、LOAD_CONSTCALL_FUNCTION三個(gè)字節(jié)碼指令,完成range(10)的操作,將一個(gè)可迭代對象壓入運(yùn)行時(shí)棧。
              2 LOAD_NAME                0 (range)
              4 LOAD_CONST               0 (10)
              6 CALL_FUNCTION            1
              8 GET_ITER
  • 緊接著通過GET_ITER字節(jié)碼指令來獲得可迭代對象的迭代器。
ceval.c

        TARGET(GET_ITER) {
            /* before: [obj]; after [getiter(obj)] */
            PyObject *iterable = TOP();
            PyObject *iter = PyObject_GetIter(iterable);
            Py_DECREF(iterable);
            SET_TOP(iter);
            if (iter == NULL)
                goto error;
            PREDICT(FOR_ITER);
            PREDICT(CALL_FUNCTION);
            DISPATCH();
        }
  • 虛擬機(jī)首先通過TOP函數(shù)獲得運(yùn)行時(shí)棧頂?shù)目傻鷮ο蟆?/li>
ceval.c

#define TOP()             (stack_pointer[-1])
  • 然后通過PyObject_GetIter函數(shù)獲得對應(yīng)的迭代器,即t->tp_iter。
Objects\abstract.c

PyObject *
PyObject_GetIter(PyObject *o)
{
   PyTypeObject *t = o->ob_type;
   getiterfunc f;

   f = t->tp_iter;
   if (f == NULL) {
       if (PySequence_Check(o))
           return PySeqIter_New(o);
       return type_error("'%.200s' object is not iterable", o);
   }
   else {
       PyObject *res = (*f)(o);
       if (res != NULL && !PyIter_Check(res)) {
           PyErr_Format(PyExc_TypeError,
                        "iter() returned non-iterator "
                        "of type '%.100s'",
                        res->ob_type->tp_name);
           Py_DECREF(res);
           res = NULL;
       }
       return res;
   }
}
  • 迭代器對象的對象類型如下:
Objects\iterobject.c

PyTypeObject PySeqIter_Type = {
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
    "iterator",                                 /* tp_name */
    sizeof(seqiterobject),                      /* tp_basicsize */
    0,                                          /* tp_itemsize */
    /* methods */
    (destructor)iter_dealloc,                   /* tp_dealloc */
    0,                                          /* tp_print */
    0,                                          /* tp_getattr */
    0,                                          /* tp_setattr */
    0,                                          /* tp_reserved */
    0,                                          /* tp_repr */
    0,                                          /* tp_as_number */
    0,                                          /* tp_as_sequence */
    0,                                          /* tp_as_mapping */
    0,                                          /* tp_hash */
    0,                                          /* tp_call */
    0,                                          /* tp_str */
    PyObject_GenericGetAttr,                    /* tp_getattro */
    0,                                          /* tp_setattro */
    0,                                          /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
    0,                                          /* tp_doc */
    (traverseproc)iter_traverse,                /* tp_traverse */
    0,                                          /* tp_clear */
    0,                                          /* tp_richcompare */
    0,                                          /* tp_weaklistoffset */
    PyObject_SelfIter,                          /* tp_iter */
    iter_iternext,                              /* tp_iternext */
    seqiter_methods,                            /* tp_methods */
    0,                                          /* tp_members */
};
  • 觀察迭代器對象的創(chuàng)建函數(shù)PySeqIter_New:
Objects\iterobject.c

PyObject *
PySeqIter_New(PyObject *seq)
{
    seqiterobject *it;

    if (!PySequence_Check(seq)) {
        PyErr_BadInternalCall();
        return NULL;
    }
    it = PyObject_GC_New(seqiterobject, &PySeqIter_Type);
    if (it == NULL)
        return NULL;
    it->it_index = 0;
    Py_INCREF(seq);
    it->it_seq = seq;
    _PyObject_GC_TRACK(it);
    return (PyObject *)it;
}
  • 可以看出可迭代對象的迭代器對象可迭代對象進(jìn)行了簡單的封裝,并通過序號it_index實(shí)現(xiàn)遍歷。
3. 迭代控制
  • 為了避免每次循環(huán)都要創(chuàng)建迭代器,源碼級的循環(huán)控制結(jié)構(gòu)一定對應(yīng)著虛擬機(jī)一級的循環(huán)控制結(jié)構(gòu)。
  • 而Python虛擬機(jī)一級的迭代循環(huán),從FOR_ITER字節(jié)碼指令開始。
>>   10 FOR_ITER                12 (to 24)
ceval.c

        PREDICTED(FOR_ITER);
        TARGET(FOR_ITER) {
            /* before: [iter]; after: [iter, iter()] *or* [] */
            PyObject *iter = TOP();
            PyObject *next = (*iter->ob_type->tp_iternext)(iter);
            if (next != NULL) {
                PUSH(next);
                PREDICT(STORE_FAST);
                PREDICT(UNPACK_SEQUENCE);
                DISPATCH();
            }
            if (PyErr_Occurred()) {
                if (!PyErr_ExceptionMatches(PyExc_StopIteration))
                    goto error;
                else if (tstate->c_tracefunc != NULL)
                    call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, f);
                PyErr_Clear();
            }
            /* iterator ended normally */
            STACKADJ(-1);
            Py_DECREF(iter);
            JUMPBY(oparg);
            PREDICT(POP_BLOCK);
            DISPATCH();
        }
  • FOR_ITER首先通過TOP()函數(shù)獲得運(yùn)行時(shí)棧頂?shù)?strong>迭代器對象,然后通過tp_iternext開始迭代:
Objects\iterobject.c

static PyObject *
iter_iternext(PyObject *iterator)
{
    seqiterobject *it;
    PyObject *seq;
    PyObject *result;

    assert(PySeqIter_Check(iterator));
    it = (seqiterobject *)iterator;
    seq = it->it_seq;
    if (seq == NULL)
        return NULL;
    if (it->it_index == PY_SSIZE_T_MAX) {
        PyErr_SetString(PyExc_OverflowError,
                        "iter index too large");
        return NULL;
    }

    result = PySequence_GetItem(seq, it->it_index);
    if (result != NULL) {
        it->it_index++;
        return result;
    }
    if (PyErr_ExceptionMatches(PyExc_IndexError) ||
        PyErr_ExceptionMatches(PyExc_StopIteration))
    {
        PyErr_Clear();
        it->it_seq = NULL;
        Py_DECREF(seq);
    }
    return NULL;
}
  • 這段代碼通過PySequence_GetItem獲取下一條元素對象返回。
  • 當(dāng)跳轉(zhuǎn)到字節(jié)碼指令JUMP_ABSOLUTE時(shí),虛擬機(jī)實(shí)現(xiàn)了字節(jié)碼的向后回退動作,而Python虛擬機(jī)也在FOR_ITER指令和JUMP_ABSOLUTE指令之間成功構(gòu)造出一個(gè)循環(huán)結(jié)構(gòu)。
ceval.c

        PREDICTED(JUMP_ABSOLUTE);
        TARGET(JUMP_ABSOLUTE) {
            JUMPTO(oparg);
4. 終止迭代
        >>   10 FOR_ITER                12 (to 24)
             12 STORE_NAME               1 (i)

  2          14 LOAD_NAME                2 (print)
             16 LOAD_CONST               1 (1)
             18 CALL_FUNCTION            1
             20 POP_TOP
             22 JUMP_ABSOLUTE           10
        >>   24 POP_BLOCK
  • 當(dāng)FOR_ITER中的result返回NULL時(shí),則循環(huán)結(jié)束,通過JUMPBY函數(shù)向前跳躍到POP_BLOCK字節(jié)碼。
ceval.c

#define JUMPBY(x)       (next_instr += (x) / sizeof(_Py_CODEUNIT))
        PREDICTED(POP_BLOCK);
        TARGET(POP_BLOCK) {
            PyTryBlock *b = PyFrame_BlockPop(f);
            UNWIND_BLOCK(b);
            DISPATCH();
        }
  • POP_BLOCK實(shí)際上是將之前申請的PyTryBlock結(jié)構(gòu)歸還給了f->f_blockstack。
ceval.c

#define UNWIND_BLOCK(b) \
    while (STACK_LEVEL() > (b)->b_level) { \
        PyObject *v = POP(); \
        Py_XDECREF(v); \
    }
  • 虛擬機(jī)會抽取在SETUP_LOOP指令處保存在PyTryBlock中的信息,并根據(jù)其中存的棧深度將運(yùn)行時(shí)?;謴?fù)到SETUP_LOOP之前的狀態(tài)。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容