大师兄的Python源码学习笔记(十四): 虚拟机中的控制流(一
2021-05-02 本文已影响0人
superkmi
大师兄的Python源码学习笔记(十三): Python虚拟机中的一般表达式(二)
大师兄的Python源码学习笔记(十五): 虚拟机中的控制流(二)
一、虚拟机中的if控制流
- 在所有编程语言中,if控制流是最简单最常用的流控制语句,如果我们编写一段简单的代码:
demo.py
a=10
if a>1:
...
elif a <= -5:
...
elif a != 10:
...
elif a ==10:
...
else:
...
- 下面是
demo.py
生成的字节码指令序列:
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
- 根据以往的学习,可以了解到
LOAD_CONST
和STORE_NAME
是在PyFrameObject对象的local名字空间添加a常量,并关联一个PyIntObject对象10。
1 0 LOAD_CONST 0 (10)
2 STORE_NAME 0 (a)
1.1 比较操作
- Python中的if控制语句根据if语句处的判断结果,决定程序的流程走向。
- 所谓判断,就是将两个对象进行比较,判断的结果,也就是比较的结果。
if a>1:
- 这段判断语句对应的字节码指令序列如下:
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所对应的的变量值对象,这里我们知道是10。 -
LOAD_CONST
从常量表consts中读取参与该分支判断操作的常量对象,这里是1。 -
COMPARE_OP
对前面两条指令获得的对象进行比较操作。 -
POP_JUMP_IF_FALSE
根据COMPARE_OP
指令的运行结果进行字节码指令的跳跃。
1.2 比较过程
- 从上面的结构可以看出,
COMPARE_OP
传入的参数是区分不同分支判断的关键。
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()两个值,并通过cmp_outcome函数获得进行比较结果。
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
指令不仅管辖着两个对象之间的比较操作,还覆盖了对象与集合之间关系的判断操作,比如在PyCmp_IN
中,使用了PySequence_Contains
函数进一步判断:
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);
}
- 而对于两个对象之间的比较,则使用了
PyObject_RichCompare
函数:
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
函数实际调用了do_richcompare
函数实现了对象的比对。
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 比较的结果
- 通过复杂的比对过程,Python会获得一个bool值作为结果。
v = res ? Py_True : Py_False;
- Py_True和Py_False是两个PyObject对象。
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其实底层和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 指令的跳跃
- 如果判断结果为False,虚拟机会执行指令跳跃操作:
10 POP_JUMP_IF_FALSE 14
- 对应的代码如下:
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();
}
- 这里实际是通过宏JUMPTO跳到下一段字节码的位置:
ceval.c
#define JUMPTO(x) (next_instr = first_instr + (x) / sizeof(_Py_CODEUNIT))
- 如果最终结果判断为TRUE,则通过JUMP_FORWARD指令跳到if控制结构的末尾。
ceval.c
TARGET(JUMP_FORWARD) {
JUMPBY(oparg);
FAST_DISPATCH();
}
- 这里实际通过JUMPBY宏实现跳转到if控制结构末尾:
ceval.c
#define JUMPBY(x) (next_instr += (x) / sizeof(_Py_CODEUNIT))