1. 程式人生 > >Python 原始碼分析之位元組碼之基本操作

Python 原始碼分析之位元組碼之基本操作

本文基於 Python 3.6.4 編譯器生成位元組碼,你可通過如下程式碼片段得到
python 原始碼對應的位元組碼

#!/usr/bin/env python
# encoding: utf-8

import sys
import dis

filename=sys.argv[1]
f = open(filename, 'rb')
content = f.read()
c = compile(content, filename, "exec")
dis.dis(c)

指令執行

所有的位元組碼都位於 Python/ceval.c 檔案

//元祖的第 i 個元素
#define GETITEM(v, i) PyTuple_GetItem((v), (i))
//調整棧頂指標 #define BASIC_STACKADJ(n) (stack_pointer += n) #define STACKADJ(n) { (void)(BASIC_STACKADJ(n), \ lltrace && prtrace(TOP(), "stackadj")); \ assert(STACK_LEVEL() <= co->co_stacksize); } //入棧 #define BASIC_PUSH(v) (*stack_pointer++ = (v))
#define PUSH(v) { (void)(BASIC_PUSH(v), \ lltrace && prtrace(TOP(), "push")); \ assert(STACK_LEVEL() <= co->co_stacksize); } //出棧 #define BASIC_POP() (*--stack_pointer) #define POP() ((void)(lltrace && prtrace(TOP(), "pop")), \
BASIC_POP()) //指向下一條指令 #define DISPATCH() \ { \ if (!_Py_atomic_load_relaxed(&eval_breaker)) { \ FAST_DISPATCH(); \ } \ continue; \ } #ifdef LLTRACE #define FAST_DISPATCH() \ { \ if (!lltrace && !_Py_TracingPossible && !PyDTrace_LINE_ENABLED()) { \ f->f_lasti = INSTR_OFFSET(); \ NEXTOPARG(); \ goto *opcode_targets[opcode]; \ } \ goto fast_next_opcode; \ } #else #define FAST_DISPATCH() \ { \ if (!_Py_TracingPossible && !PyDTrace_LINE_ENABLED()) { \ f->f_lasti = INSTR_OFFSET(); \ NEXTOPARG(); \ goto *opcode_targets[opcode]; \ } \ goto fast_next_opcode; \ } #endif #define JUMPBY(x) (next_instr += (x) / sizeof(_Py_CODEUNIT)) #define JUMPTO(x) (next_instr = first_instr + (x) / sizeof(_Py_CODEUNIT)) typedef uint16_t _Py_CODEUNIT
#!/usr/bin/env python
# encoding: utf-8

i = 1
s = "Python"
d = {}
l = {}

對應的位元組碼為

  4           0 LOAD_CONST               0 (1)
              2 STORE_NAME               0 (i)

  5           4 LOAD_CONST               1 ('Python')
              6 STORE_NAME               1 (s)

  6           8 BUILD_MAP                0
             10 STORE_NAME               2 (d)
             12 LOAD_CONST               2 (None)
             14 RETURN_VALUE

注:第一列為原始碼行號,第二列為指令索引, 第三列為指令,第四列為指令的引數,第五列為指令引數對應的值(提示)。

TARGET(LOAD_CONST) {
    # 從常量表中獲取索引為 oparg 的元素,其值為 value
    PyObject *value = GETITEM(consts, oparg);
    Py_INCREF(value);
    PUSH(value);
    FAST_DISPATCH();
}
TARGET(STORE_NAME) {
    //從符號表獲取第 oparg 個元素作為變數名 name
    PyObject *name = GETITEM(names, oparg);
    //將棧頂元素賦值給 v,並將棧指標減 1
    PyObject *v = POP();
    PyObject *ns = f->f_locals;
    int err;
    if (ns == NULL) {
        PyErr_Format(PyExc_SystemError,
                     "no locals found when storing %R", name);
        Py_DECREF(v);
        goto error;
    }
    if (PyDict_CheckExact(ns))
        //f->f_locals 中設定 name = v
        err = PyDict_SetItem(ns, name, v);
    else
        //ns.name = v
        err = PyObject_SetItem(ns, name, v);
    //引用減一
    Py_DECREF(v);
    if (err != 0)
        goto error;
    DISPATCH();
}
#define PEEK(n)           (stack_pointer[-(n)])
TARGET(BUILD_MAP) {
    Py_ssize_t i;
    //建立一個字典,初始元素個數為 oparg
    PyObject *map = _PyDict_NewPresized((Py_ssize_t)oparg);
    if (map == NULL)
        goto error;
    for (i = oparg; i > 0; i--) {
        int err;
        //找到 key
        PyObject *key = PEEK(2*i);
        //找到 value
        PyObject *value = PEEK(2*i - 1);
        //設定 map[key] = value
        err = PyDict_SetItem(map, key, value);
        if (err != 0) {
            Py_DECREF(map);
            goto error;
        }
    }

    while (oparg--) {
        Py_DECREF(POP());
        Py_DECREF(POP());
    }
    PUSH(map);
    DISPATCH();
}
TARGET(BUILD_LIST) {
    //建立一個列表,初始元素個數為 oparg
    PyObject *list =  PyList_New(oparg);
    if (list == NULL)
        goto error;
    //從後往前
    while (--oparg >= 0) {
        //從棧中彈出一個元素
        PyObject *item = POP();
        //設定 list[oparg] = item
        PyList_SET_ITEM(list, oparg, item);
    }
    //list 壓棧
    PUSH(list);
    DISPATCH();
}

TARGET(RETURN_VALUE) {
    retval = POP();
    why = WHY_RETURN;
    goto fast_block_end;
}

例 2

#!/usr/bin/env python
# encoding: utf-8

a = 1
b = "Python"
c = a + b
  4           0 LOAD_CONST               0 (1)
              3 STORE_NAME               0 (a)

  5           6 LOAD_CONST               1 ('Python')
              9 STORE_NAME               1 (b)

  6          12 LOAD_NAME                0 (a)
             15 LOAD_NAME                1 (b)
             18 BINARY_ADD
             19 STORE_NAME               2 (c)
             22 LOAD_CONST               2 (None)
             25 RETURN_VALUE
TARGET(LOAD_NAME) {
    //從符號表載入中第 oparg 個元素 name,依次從區域性變量表,全域性變量表,
    //內建變量表中查詢 name 對應找到值,如果找到將該值壓棧,如果沒有找到拋異常。
    PyObject *name = GETITEM(names, oparg);
    PyObject *locals = f->f_locals;
    PyObject *v;
    //確保區域性變量表存在 name
    if (locals == NULL) {
        PyErr_Format(PyExc_SystemError,
                     "no locals when loading %R", name);
        goto error;
    }
    //如果是字典物件,從區域性變量表中獲取 name 對應的值
    if (PyDict_CheckExact(locals)) {
        v = PyDict_GetItem(locals, name);
        Py_XINCREF(v);
    }
    else {
        v = PyObject_GetItem(locals, name);
        if (v == NULL) {
            if (!PyErr_ExceptionMatches(PyExc_KeyError))
                goto error;
            PyErr_Clear();
        }
    }
    //如果區域性變量表不存在該變數,從全域性變數中查詢
    if (v == NULL) {
        v = PyDict_GetItem(f->f_globals, name);
        Py_XINCREF(v);
        if (v == NULL) {
            # 如果全域性變量表中也不存在,從內建變量表中查詢
            if (PyDict_CheckExact(f->f_builtins)) {
                v = PyDict_GetItem(f->f_builtins, name);
                if (v == NULL) {
                    format_exc_check_arg(
                                PyExc_NameError,
                                NAME_ERROR_MSG, name);
                    goto error;
                }
                Py_INCREF(v);
            }
            else {
                v = PyObject_GetItem(f->f_builtins, name);
                if (v == NULL) {
                    if (PyErr_ExceptionMatches(PyExc_KeyError))
                        format_exc_check_arg(
                                    PyExc_NameError,
                                    NAME_ERROR_MSG, name);
                    goto error;
                }
            }
        }
    }
    PUSH(v);
    DISPATCH();
}
TARGET(BINARY_ADD) {
    //從棧中彈出右邊的變數
    PyObject *right = POP();
    //獲取棧頂元素,可見入棧從左往右
    PyObject *left = TOP();
    PyObject *sum;
    if (PyUnicode_CheckExact(left) &&
             PyUnicode_CheckExact(right)) {
        //字串連線
        sum = unicode_concatenate(left, right, f, next_instr);
        /* unicode_concatenate consumed the ref to left */
    }
    else {
        //數字相加
        sum = PyNumber_Add(left, right);
        Py_DECREF(left);
    }
    Py_DECREF(right);
    //直接將棧頂元素設定為疊加之後的值,避免了兩次棧操作
    SET_TOP(sum);
    if (sum == NULL)
        goto error;
    DISPATCH();
}
#!/usr/bin/env python
# encoding: utf-8
c = { "1" : "a", "2": "b" }
c = [1, 2, 3]
  4           0 BUILD_MAP                2
              3 LOAD_CONST               0 ('a')
              6 LOAD_CONST               1 ('1')
              9 STORE_MAP
             10 LOAD_CONST               2 ('b')
             13 LOAD_CONST               3 ('2')
             16 STORE_MAP
             17 STORE_NAME               0 (c)

  5          20 LOAD_CONST               4 (1)
             23 LOAD_CONST               5 (2)
             26 LOAD_CONST               6 (3)
             29 BUILD_LIST               3
             32 STORE_NAME               0 (c)
             35 LOAD_CONST               7 (None)
             38 RETURN_VALUE

可見 map 是載入一個元素,寫入 map,再載入一個元素,再寫入,依次類推
而 list 一次將所有元素寫入棧,然後寫入 list

哈哈,這裡 list 會不會有棧溢位攻擊呢? 我試了下 500 元素構造 list
也是壓棧 500 次,之後再加入 list

函式呼叫

#!/usr/bin/env python
# encoding: utf-8

a = 2
print(a)
  4           0 LOAD_CONST               0 (2)
              2 STORE_NAME               0 (a)

  5           4 LOAD_NAME                1 (print)
              6 LOAD_NAME                0 (a)
              8 CALL_FUNCTION            1
             10 POP_TOP
             12 LOAD_CONST               1 (None)
             14 RETURN_VALUE
PREDICTED(CALL_FUNCTION);
TARGET(CALL_FUNCTION) {
    PyObject **sp, *res;
    PCALL(PCALL_ALL);
    sp = stack_pointer;
    res = call_function(&sp, oparg, NULL);
    stack_pointer = sp;
    PUSH(res);
    if (res == NULL) {
        goto error;
    }
    DISPATCH();
}
#define Py_TYPE(ob)             (((PyObject*)(ob))->ob_type)


static PyObject *
call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *kwnames)
{
    PyObject **pfunc = (*pp_stack) - oparg - 1;
    PyObject *func = *pfunc;
    PyObject *x, *w;
    Py_ssize_t nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames);
    Py_ssize_t nargs = oparg - nkwargs;
    PyObject **stack;

    /* Always dispatch PyCFunction first, because these are
       presumed to be the most frequent callable object.
    */
    if (PyCFunction_Check(func)) {
        PyThreadState *tstate = PyThreadState_GET();

        PCALL(PCALL_CFUNCTION);

        stack = (*pp_stack) - nargs - nkwargs;
        C_TRACE(x, _PyCFunction_FastCallKeywords(func, stack, nargs, kwnames));
    }
    else {
        if (PyMethod_Check(func) && PyMethod_GET_SELF(func) != NULL) {
            /* optimize access to bound methods */
            PyObject *self = PyMethod_GET_SELF(func);
            PCALL(PCALL_METHOD);
            PCALL(PCALL_BOUND_METHOD);
            Py_INCREF(self);
            func = PyMethod_GET_FUNCTION(func);
            Py_INCREF(func);
            Py_SETREF(*pfunc, self);
            nargs++;
        }
        else {
            Py_INCREF(func);
        }

        stack = (*pp_stack) - nargs - nkwargs;

        if (PyFunction_Check(func)) {
            x = fast_function(func, stack, nargs, kwnames);
        }
        else {
            x = _PyObject_FastCallKeywords(func, stack, nargs, kwnames);
        }

        Py_DECREF(func);
    }

    assert((x != NULL) ^ (PyErr_Occurred() != NULL));

    /* Clear the stack of the function object.  Also removes
       the arguments in case they weren't consumed already
       (fast_function() and err_args() leave them on the stack).
     */
    while ((*pp_stack) > pfunc) {
        w = EXT_POP(*pp_stack);
        Py_DECREF(w);
        PCALL(PCALL_POP);
    }

    return x;
}

if 語句

#!/usr/bin/env python
# encoding: utf-8

a = 2
if a > 1:
    a = 0

編譯之後

  4           0 LOAD_CONST               0 (2)
              2 STORE_NAME               0 (a)

  5           4 LOAD_NAME                0 (a)
              6 LOAD_CONST               1 (1)
              8 COMPARE_OP               4 (>)
             10 POP_JUMP_IF_FALSE       16

  6          12 LOAD_CONST               2 (0)
             14 STORE_NAME               0 (a)
        >>   16 LOAD_CONST               3 (None)
             18 RETURN_VALUE
static PyObject * cmp_outcome(int op, PyObject *v, PyObject *w)
{
    int res = 0;
    switch (op) {
    //...
    default:
        return PyObject_RichCompare(v, w, op);
    }
    v = res ? Py_True : Py_False;
    Py_INCREF(v);
    return v;
}

PyObject *
PyObject_RichCompare(PyObject *v, PyObject *w, int op)
{
    PyObject *res;

    assert(Py_LT <= op && op <= Py_GE);
    if (v == NULL || w == NULL) {
        if (!PyErr_Occurred())
            PyErr_BadInternalCall();
        return NULL;
    }
    if (Py_EnterRecursiveCall(" in comparison"))
        return NULL;
    res = do_richcompare(v, w, op);
    Py_LeaveRecursiveCall();
    return res;
}

//由下可知實際呼叫的是物件型別的  ob_type->tp_richcompare
static PyObject *
do_richcompare(PyObject *v, PyObject *w, int op)
{
    richcmpfunc f;
    PyObject *res;
    int checked_reverse_op = 0;

    if (v->ob_type != w->ob_type &&
        PyType_IsSubtype(w->ob_type, v->ob_type) &&
        (f = w->ob_type->tp_richcompare) != NULL) {
        checked_reverse_op = 1;
        res = (*f)(w, v, _Py_SwappedOp[op]);
        if (res != Py_NotImplemented)
            return res;
        Py_DECREF(res);
    }
    if ((f = v->ob_type->tp_richcompare) != NULL) {
        res = (*f)(v, w, op);
        if (res != Py_NotImplemented)
            return res;
        Py_DECREF(res);
    }
    if (!checked_reverse_op && (f = w->ob_type->tp_richcompare) != NULL) {
        res = (*f)(w, v, _Py_SwappedOp[op]);
        if (res != Py_NotImplemented)
            return res;
        Py_DECREF(res);
    }
    /* If neither object implements it, provide a sensible default
       for == and !=, but raise an exception for ordering. */
    switch (op) {
    case Py_EQ:
        res = (v == w) ? Py_True : Py_False;
        break;
    case Py_NE:
        res = (v != w) ? Py_True : Py_False;
        break;
    default:
        PyErr_Format(PyExc_TypeError,
                     "'%s' not supported between instances of '%.100s' and '%.100s'",
                     opstrings[op],
                     v->ob_type->tp_name,
                     w->ob_type->tp_name);
        return NULL;
    }
    Py_INCREF(res);
    return res;
}

TARGET(COMPARE_OP) {
    //彈出右值
    PyObject *right = POP();
    //取左值
    PyObject *left = TOP();
    //
    PyObject *res = cmp_outcome(oparg, left, right);
    Py_DECREF(left);
    Py_DECREF(right);
    //結果寫入棧頂
    SET_TOP(res);
    if (res == NULL)
        goto error;
    //沒有找到 PRED_XXXX 相關程式碼,但從 C 語言的經驗來看應該是分支預測相關,
    //當然這與我們掌握位元組碼本身關係也不是特別大,暫且跳過。
    PREDICT(POP_JUMP_IF_FALSE);
    PREDICT(POP_JUMP_IF_TRUE);
    DISPATCH();
}

#if defined(DYNAMIC_EXECUTION_PROFILE) || USE_COMPUTED_GOTOS
#define PREDICT(op)             if (0) goto PRED_##op
#else
#define PREDICT(op) \
    do{ \
        _Py_CODEUNIT word = *next_instr; \
        opcode = _Py_OPCODE(word); \
        if (opcode == op){ \
            oparg = _Py_OPARG(word); \
            next_instr++; \
            goto PRED_##op; \
        } \
    } while(0)
#endif
#define PREDICTED(op)           PRED_##op:
PREDICTED(POP_JUMP_IF_FALSE);
TARGET(POP_JUMP_IF_FALSE) {
    PyObject *cond = POP();
    int err;
    //如果為 True,繼續執行
    if (cond == Py_True) {
        Py_DECREF(cond);
        FAST_DISPATCH();
    }
    //如果為 False,跳轉到引數 oparg 的位置
    if (cond == Py_False) {
        Py_DECREF(cond);
        JUMPTO(oparg);
        FAST_DISPATCH();
    }
    //如果不是 bool 型別,大於 0,設定 err = 0
    //等於 0,跳轉到引數 oparg 位置,小於 0,報錯
    err = PyObject_IsTrue(cond);
    Py_DECREF(cond);
    if (err > 0)
        err = 0;
    else if (err == 0)
        JUMPTO(oparg);
    else
        goto error;
    DISPATCH();
}

PREDICTED(POP_JUMP_IF_TRUE);
TARGET(POP_JUMP_IF_TRUE) {
    PyObject *cond = POP();
    int err;
    if (cond == Py_False) {
        Py_DECREF(cond);
        FAST_DISPATCH();
    }
    if (cond == Py_True) {
        Py_DECREF(cond);
        JUMPTO(oparg);
        FAST_DISPATCH();
    }
    err = PyObject_IsTrue(cond);
    Py_DECREF(cond);
    if (err > 0) {
        err = 0;
        JUMPTO(oparg);
    }
    else if (err == 0)
        ;
    else
        goto error;
    DISPATCH();
}

for 語句

#!/usr/bin/env python
# encoding: utf-8

a = [1, 2, 3]
for e in a:
    b = e
  9           0 LOAD_CONST               0 (1)
              2 LOAD_CONST               1 (2)
              4 LOAD_CONST               2 (3)
              6 BUILD_LIST               3
              8 STORE_NAME               0 (a)

 10          10 SETUP_LOOP              16 (to 28)
             12 LOAD_NAME                0 (a)
             14 GET_ITER
             # 8/2 = 4,向前跳 4 條指令,本例到了  POP_BLOCK
        >>   16 FOR_ITER                 8 (to 26)
             18 STORE_NAME               1 (e)

 11          20 LOAD_NAME                1 (e)
             22 STORE_NAME               2 (b)
             24 JUMP_ABSOLUTE           16  //調到 FOR_ITER
        >>   26 POP_BLOCK
        >>   28 LOAD_CONST               3 (None)
             30 RETURN_VALUE
const _Py_CODEUNIT *first_instr;
const _Py_CODEUNIT *next_instr;
first_instr = (_Py_CODEUNIT *) PyBytes_AS_STRING(co->co_code);
#define INSTR_OFFSET()  (sizeof(_Py_CODEUNIT) * (int)(next_instr - first_instr))
TARGET(SETUP_LOOP) {
    PyFrame_BlockSetup(f, opcode, INSTR_OFFSET() + oparg,
                       STACK_LEVEL());
    DISPATCH();
}

//Objects/frameobject.c
void
PyFrame_BlockSetup(PyFrameObject *f, int type, int handler, int level)
{
    PyTryBlock *b;
    //哈哈,還有程式碼塊溢位 #define CO_MAXBLOCKS 20
    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;
}

//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;

typedef struct _frame {
     //....
    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;
TARGET(GET_ITER) {
    /* before: [obj]; after [getiter(obj)] */
    //獲取棧頂元素
    PyObject *iterable = TOP();
    //獲取棧頂元素的迭代器,對於 Python 來說,每個物件的類似都是
    //PyTypeObject,而 PyTypeObject 都有一個成員 tp_iter 表示其迭代器。
    //此處不再詳述,參考 Objects/abstract.c
    PyObject *iter = PyObject_GetIter(iterable);
    Py_DECREF(iterable);
    //用迭代器替代原有棧頂元素
    SET_TOP(iter);
    if (iter == NULL)
        goto error;
    PREDICT(FOR_ITER);
    PREDICT(CALL_FUNCTION);
    DISPATCH();
}

PREDICTED(FOR_ITER);
TARGET(FOR_ITER) {
    /* before: [iter]; after: [iter, iter()] *or* [] */
    //由 GET_ITER 得知,當前棧頂為 iter
    PyObject *iter = TOP();
    //獲取下一個元素
    PyObject *next = (*iter->ob_type->tp_iternext)(iter);
    if (next != NULL) {
        //壓棧
        PUSH(next);
        PREDICT(STORE_FAST);
        PREDICT(UNPACK_SEQUENCE);
        //continue 的作用是繼續執行下一條指令
        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();
}
        PREDICTED(JUMP_ABSOLUTE);
        TARGET(JUMP_ABSOLUTE) {
            JUMPTO(oparg);
#if FAST_LOOPS
            /* Enabling this path speeds-up all while and for-loops by bypassing
               the per-loop checks for signals.  By default, this should be turned-off
               because it prevents detection of a control-break in tight loops like
               "while 1: pass".  Compile with this option turned-on when you need
               the speed-up and do not need break checking inside tight loops (ones
               that contain only instructions ending with FAST_DISPATCH).
            */
            FAST_DISPATCH();
#else
            DISPATCH();
#endif
        }

PREDICTED(POP_BLOCK);
TARGET(POP_BLOCK) {
    PyTryBlock *b = PyFrame_BlockPop(f);
    UNWIND_BLOCK(b);
    DISPATCH();
}

PyTryBlock *
PyFrame_BlockPop(PyFrameObject *f)
{
    PyTryBlock *b;
    if (f->f_iblock <= 0)
        Py_FatalError("XXX block stack underflow");
    //將程式碼塊深度減一
    b = &f->f_blockstack[--f->f_iblock];
    return b;
}

因此,整個 for 迴圈的結構如下

 SETUP_LOOP
 ...
 GET_ITER
 FOR_ITER
 ...
 JUMP_ABSOLUTE
 POP_BLOCK

在建立 for 迴圈的時候 b = &f->f_blockstack[f->f_iblock++];

for 迴圈執行完的時候 b = &f->f_blockstack[–f->f_iblock];

最後,

#!/usr/bin/env python
# encoding: utf-8

for i in range(10):
    for i in range(10):
        for i in range(10):
            for i in range(10):
                for i in range(10):
                    for i in range(10):
                        for i in range(10):
                            for i in range(10):
                                for i in range(10):
                                    for i in range(10):
                                        for i in range(10):
                                            for i in range(10):
                                                for i in range(10):
                                                    for i in range(10):
                                                        for i in range(10):
                                                            for i in range(10):
                                                                for i in range(10):
                                                                    for i in range(10):
                                                                        a = 1

這估計是一道非常難的 python 面試題了, 猜猜結果是什麼,哈哈,如果換成 21 層 for
迴圈呢?

while 迴圈

#!/usr/bin/env python
# encoding: utf-8
i = 0
while i > 10:
    i += 1
    if i > 5:
        continue
    if i == 9:
        break
    b = 1

位元組碼

  9           0 LOAD_CONST               0 (0)
              2 STORE_NAME               0 (i)

 10           4 SETUP_LOOP              44 (to 50)
        >>    6 LOAD_NAME                0 (i)
              8 LOAD_CONST               1 (10)
             10 COMPARE_OP               0 (<)
             12 POP_JUMP_IF_FALSE       48

 11          14 LOAD_NAME                0 (i)
             16 LOAD_CONST               2 (1)
             18 INPLACE_ADD
             20 STORE_NAME               0 (i)

 12          22 LOAD_NAME                0 (i)
             24 LOAD_CONST               3 (5)
             26 COMPARE_OP               4 (>)
             28 POP_JUMP_IF_FALSE       32

 13          30 JUMP_ABSOLUTE            6    //continue

 14     >>   32 LOAD_NAME                0 (i)
             34 LOAD_CONST               4 (9)
             36 COMPARE_OP               2 (==)
             38 POP_JUMP_IF_FALSE       42

 15          40 BREAK_LOOP

 16     >>   42 LOAD_CONST               2 (1)
             44 STORE_NAME               1 (b)
             46 JUMP_ABSOLUTE            6
        >>   48 POP_BLOCK
        >>   50 LOAD_CONST               5 (None)
             52 RETURN_VALUE
TARGET(BREAK_LOOP) {
    why = WHY_BREAK;
    goto fast_block_end;
}


fast_block_end:
        /* Unwind stacks if a (pseudo) exception occurred */
        while (why != WHY_NOT && f->f_iblock > 0) {
            /* Peek at the current block. */
            PyTryBlock *b = &f->f_blockstack[f->f_iblock - 1];

            /* Now we have to pop the block. */
            f->f_iblock--;

            if (b->b_type == EXCEPT_HANDLER) {
                UNWIND_EXCEPT_HANDLER(b);
                continue;
            }
            UNWIND_BLOCK(b);
            //執行這裡
            if (b->b_type == SETUP_LOOP && why == WHY_BREAK) {
                //設定 why
                why = WHY_NOT;
                //調整到 b->b_handler,b->b_handler 實際上就是 SETUP_LOOP 的引數
                JUMPTO(b->b_handler);
                break;
            }

剩下的都是熟悉的指令,熟悉的面孔。

因此,整個 for 迴圈的結構如下

 SETUP_LOOP
 ...
 COMPARE_OP
 POP_JUMP_IF_FALSE
 ...
 JUMP_ABSOLUTE
 POP_BLOCK

在建立 for 迴圈的時候 b = &f->f_blockstack[f->f_iblock++];

for 迴圈執行完的時候 b = &f->f_blockstack[–f->f_iblock];