大師兄的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_CONST和CALL_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)。