大師兄的Python源碼學(xué)習(xí)筆記(十三): Python虛擬機(jī)中的一般表達(dá)式(二)
大師兄的Python源碼學(xué)習(xí)筆記(十五): 虛擬機(jī)中的控制流(二)
一、虛擬機(jī)中的if控制流
- 在所有編程語言中,if控制流是最簡(jiǎn)單最常用的流控制語句,如果我們編寫一段簡(jiǎn)單的代碼:
demo.py
a=10
if a>1:
...
elif a <= -5:
...
elif a != 10:
...
elif a ==10:
...
else:
...
- 下面是
demo.py生成的字節(jié)碼指令序列:
1 0 LOAD_CONST 0 (10)
2 STORE_NAME 0 (a)
2 4 LOAD_NAME 0 (a)
6 LOAD_CONST 1 (1)
8 COMPARE_OP 4 (>)
10 POP_JUMP_IF_FALSE 14
3 12 JUMP_FORWARD 30 (to 44)
4 >> 14 LOAD_NAME 0 (a)
16 LOAD_CONST 2 (-5)
18 COMPARE_OP 1 (<=)
20 POP_JUMP_IF_FALSE 24
5 22 JUMP_FORWARD 20 (to 44)
6 >> 24 LOAD_NAME 0 (a)
26 LOAD_CONST 0 (10)
28 COMPARE_OP 3 (!=)
30 POP_JUMP_IF_FALSE 34
7 32 JUMP_FORWARD 10 (to 44)
8 >> 34 LOAD_NAME 0 (a)
36 LOAD_CONST 0 (10)
38 COMPARE_OP 2 (==)
40 POP_JUMP_IF_FALSE 44
9 42 JUMP_FORWARD 0 (to 44)
11 >> 44 LOAD_CONST 3 (None)
46 RETURN_VALUE
- 根據(jù)以往的學(xué)習(xí),可以了解到
LOAD_CONST和STORE_NAME是在PyFrameObject對(duì)象的local名字空間添加a常量,并關(guān)聯(lián)一個(gè)PyIntObject對(duì)象10。
1 0 LOAD_CONST 0 (10)
2 STORE_NAME 0 (a)
1.1 比較操作
- Python中的if控制語句根據(jù)if語句處的判斷結(jié)果,決定程序的流程走向。
- 所謂判斷,就是將兩個(gè)對(duì)象進(jìn)行比較,判斷的結(jié)果,也就是比較的結(jié)果。
if a>1:
- 這段判斷語句對(duì)應(yīng)的字節(jié)碼指令序列如下:
2 4 LOAD_NAME 0 (a)
6 LOAD_CONST 1 (1)
8 COMPARE_OP 4 (>)
10 POP_JUMP_IF_FALSE 14
-
LOAD_NAME從local名字空間獲得變量名a所對(duì)應(yīng)的的變量值對(duì)象,這里我們知道是10。 -
LOAD_CONST從常量表consts中讀取參與該分支判斷操作的常量對(duì)象,這里是1。 -
COMPARE_OP對(duì)前面兩條指令獲得的對(duì)象進(jìn)行比較操作。 -
POP_JUMP_IF_FALSE根據(jù)COMPARE_OP指令的運(yùn)行結(jié)果進(jìn)行字節(jié)碼指令的跳躍。
1.2 比較過程
- 從上面的結(jié)構(gòu)可以看出,
COMPARE_OP傳入的參數(shù)是區(qū)分不同分支判斷的關(guān)鍵。
ceval.c
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;
PREDICT(POP_JUMP_IF_FALSE);
PREDICT(POP_JUMP_IF_TRUE);
DISPATCH();
}
- 這段代碼從棧中獲取了左TOP()右POP()兩個(gè)值,并通過cmp_outcome函數(shù)獲得進(jìn)行比較結(jié)果。
ceval.c
static PyObject *
cmp_outcome(int op, PyObject *v, PyObject *w)
{
int res = 0;
switch (op) {
case PyCmp_IS:
res = (v == w);
break;
case PyCmp_IS_NOT:
res = (v != w);
break;
case PyCmp_IN:
res = PySequence_Contains(w, v);
if (res < 0)
return NULL;
break;
case PyCmp_NOT_IN:
res = PySequence_Contains(w, v);
if (res < 0)
return NULL;
res = !res;
break;
case PyCmp_EXC_MATCH:
if (PyTuple_Check(w)) {
Py_ssize_t i, length;
length = PyTuple_Size(w);
for (i = 0; i < length; i += 1) {
PyObject *exc = PyTuple_GET_ITEM(w, i);
if (!PyExceptionClass_Check(exc)) {
PyErr_SetString(PyExc_TypeError,
CANNOT_CATCH_MSG);
return NULL;
}
}
}
else {
if (!PyExceptionClass_Check(w)) {
PyErr_SetString(PyExc_TypeError,
CANNOT_CATCH_MSG);
return NULL;
}
}
res = PyErr_GivenExceptionMatches(v, w);
break;
default:
return PyObject_RichCompare(v, w, op);
}
v = res ? Py_True : Py_False;
Py_INCREF(v);
return v;
}
- 從上面的代碼中,我們可以看到
COMPARE_OP指令不僅管轄著兩個(gè)對(duì)象之間的比較操作,還覆蓋了對(duì)象與集合之間關(guān)系的判斷操作,比如在PyCmp_IN中,使用了PySequence_Contains函數(shù)進(jìn)一步判斷:
case PyCmp_IN:
res = PySequence_Contains(w, v);
if (res < 0)
return NULL;
break;
Objects\abstract.c
/* Return -1 if error; 1 if ob in seq; 0 if ob not in seq.
* Use sq_contains if possible, else defer to _PySequence_IterSearch().
*/
int
PySequence_Contains(PyObject *seq, PyObject *ob)
{
Py_ssize_t result;
PySequenceMethods *sqm = seq->ob_type->tp_as_sequence;
if (sqm != NULL && sqm->sq_contains != NULL)
return (*sqm->sq_contains)(seq, ob);
result = _PySequence_IterSearch(seq, ob, PY_ITERSEARCH_CONTAINS);
return Py_SAFE_DOWNCAST(result, Py_ssize_t, int);
}
- 而對(duì)于兩個(gè)對(duì)象之間的比較,則使用了
PyObject_RichCompare函數(shù):
Objects\object.c
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;
}
- 而
PyObject_RichCompare函數(shù)實(shí)際調(diào)用了do_richcompare函數(shù)實(shí)現(xiàn)了對(duì)象的比對(duì)。
Objects\object.c
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;
}
1.3 比較的結(jié)果
- 通過復(fù)雜的比對(duì)過程,Python會(huì)獲得一個(gè)bool值作為結(jié)果。
v = res ? Py_True : Py_False;
- Py_True和Py_False是兩個(gè)PyObject對(duì)象。
Include\boolobject.h
/* Don't use these directly */
PyAPI_DATA(struct _longobject) _Py_FalseStruct, _Py_TrueStruct;
/* Use these macros */
#define Py_False ((PyObject *) &_Py_FalseStruct)
#define Py_True ((PyObject *) &_Py_TrueStruct)
- 而Py_True和Py_False其實(shí)底層和c語言一樣就是1和0。
Objects\boolobject.c
PyTypeObject PyBool_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"bool",
sizeof(struct _longobject),
0,
0, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
bool_repr, /* tp_repr */
&bool_as_number, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
bool_repr, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
bool_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
&PyLong_Type, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
bool_new, /* tp_new */
};
/* The objects representing bool values False and True */
struct _longobject _Py_FalseStruct = {
PyVarObject_HEAD_INIT(&PyBool_Type, 0)
{ 0 }
};
struct _longobject _Py_TrueStruct = {
PyVarObject_HEAD_INIT(&PyBool_Type, 1)
{ 1 }
};
1.4 指令的跳躍
- 如果判斷結(jié)果為False,虛擬機(jī)會(huì)執(zhí)行指令跳躍操作:
10 POP_JUMP_IF_FALSE 14
- 對(duì)應(yīng)的代碼如下:
ceval.c
PREDICTED(POP_JUMP_IF_FALSE);
TARGET(POP_JUMP_IF_FALSE) {
PyObject *cond = POP();
int err;
if (cond == Py_True) {
Py_DECREF(cond);
FAST_DISPATCH();
}
if (cond == Py_False) {
Py_DECREF(cond);
JUMPTO(oparg);
FAST_DISPATCH();
}
err = PyObject_IsTrue(cond);
Py_DECREF(cond);
if (err > 0)
;
else if (err == 0)
JUMPTO(oparg);
else
goto error;
DISPATCH();
}
- 這里實(shí)際是通過宏JUMPTO跳到下一段字節(jié)碼的位置:
ceval.c
#define JUMPTO(x) (next_instr = first_instr + (x) / sizeof(_Py_CODEUNIT))
- 如果最終結(jié)果判斷為TRUE,則通過JUMP_FORWARD指令跳到if控制結(jié)構(gòu)的末尾。
ceval.c
TARGET(JUMP_FORWARD) {
JUMPBY(oparg);
FAST_DISPATCH();
}
- 這里實(shí)際通過JUMPBY宏實(shí)現(xiàn)跳轉(zhuǎn)到if控制結(jié)構(gòu)末尾:
ceval.c
#define JUMPBY(x) (next_instr += (x) / sizeof(_Py_CODEUNIT))