1、IF语句
a = 1
if a > 10:
print('a > 10')
elif a<= -2:
print('a <= -2')
else:
print('Unkonw a')
const : (1, 10, 'a > 10', 2, 'a <= -2', 'Unkonw a', None, -2)
name : ('a', 'print')
1 0 LOAD_CONST 0 (1)
3 STORE_NAME 0 (a)
2 6 LOAD_NAME 0 (a)
9 LOAD_CONST 1 (10)
12 COMPARE_OP 4 (>)
15 POP_JUMP_IF_FALSE 31
3 18 LOAD_NAME 1 (print)
21 LOAD_CONST 2 ('a > 10')
24 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
27 POP_TOP
28 JUMP_FORWARD 35 (to 66)
4 >> 31 LOAD_NAME 0 (a)
34 LOAD_CONST 7 (-2)
37 COMPARE_OP 1 (<=)
40 POP_JUMP_IF_FALSE 56
5 43 LOAD_NAME 1 (print)
46 LOAD_CONST 4 ('a <= -2')
49 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
52 POP_TOP
53 JUMP_FORWARD 10 (to 66)
7 >> 56 LOAD_NAME 1 (print)
59 LOAD_CONST 5 ('Unkonw a')
62 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
65 POP_TOP
>> 66 LOAD_CONST 6 (None)
69 RETURN_VALUE
a = 1 不再分析。
if语句的汇编指令与《Python源码剖析》中是不同的。
if a > 10: 编译为汇编指令 LOAD_NAME LOAD_CONST COMPARE_OP POP_JUMP_IF_FALSE
需注意COMPARE_OP,分为快速通道与慢速通道,若二者皆为整数且为内置操作符,直接比较[1];否则进入慢速通道[2]。慢速通道,可以比较不同类型的对象,甚至元素与集合的关系,比如 if i in a_list。比较操作返回值为[3]Py_True或者Py_False,同样为PyObject对象。会将判断结果压入栈中。
case COMPARE_OP:
if (PyInt_CheckExact(w) && PyInt_CheckExact(v)) {
switch (oparg) {
case PyCmp_LT: res = a < b; break; // [1]
……
default: goto slow_compare;
}
x = res ? Py_True : Py_False; // [3]
}
else {
slow_compare:
x = cmp_outcome(oparg, v, w); // [2]
}
……
PREDICT(POP_JUMP_IF_FALSE); // [4]
PREDICT(POP_JUMP_IF_TRUE);
continue;
此外,
COMPARE_OP中会预测下一条指令,因为有些指令是经常一起出现的,这样可以加快执行。
#define PREDICT(op) if (*next_instr == op) goto PRED_##op
POP_JUMP_IF_FALSE 比较简单,如果栈中的判断结果取出并判断,相符则跳转到参数所指位置[1]。
case POP_JUMP_IF_FALSE:
w = POP();
if (w == Py_True) {
goto fast_next_opcode;
}
if (w == Py_False) {
JUMPTO(oparg); // [1]
goto fast_next_opcode;
}
print('a > 10')除去打印指令,还有
JUMP_FORWARD,跳转到if判断结束位置。
case JUMP_FORWARD:
JUMPBY(oparg);
goto fast_next_opcode;
2、FOR循环
l = ['efei', 'me']
for i in l:
print(i)
const : ('efei', 'me', None)
name : ('l', 'i', 'print')
1 0 LOAD_CONST 0 ('efei')
3 LOAD_CONST 1 ('me')
6 BUILD_LIST 2
9 STORE_NAME 0 (l)
2 12 SETUP_LOOP 24 (to 39)
15 LOAD_NAME 0 (l)
18 GET_ITER
>> 19 FOR_ITER 16 (to 38)
22 STORE_NAME 1 (i)
3 25 LOAD_NAME 2 (print)
28 LOAD_NAME 1 (i)
31 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
34 POP_TOP
35 JUMP_ABSOLUTE 19
>> 38 POP_BLOCK
>> 39 LOAD_CONST 2 (None)
42 RETURN_VALUE
for i in l:
SETUP_LOOP,首先建立循环,用到PyFrame_BlockSetup,并且抛出异常等也是这种方式,关键是用到了PyTryBlock,并且对其进行设置,保存运行时信息,该结构体其实放到了运行时帧PyFrameObject的f_blockstack中。
case SETUP_LOOP:
case SETUP_EXCEPT:
case SETUP_FINALLY:
PyFrame_BlockSetup(f, opcode, INSTR_OFFSET() + oparg, STACK_LEVEL());
PyFrame_BlockSetup(PyFrameObject *f, int type, int handler, int level)
{
PyTryBlock *b;
b = &f->f_blockstack[f->f_iblock++];
b->b_type = type;
b->b_level = level;
b->b_handler = handler;
}
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;
GET_ITER,然后获取迭代器。
case GET_ITER:
x = PyObject_GetIter(v); // 获取迭代器所指对象
if (x != NULL) {
SET_TOP(x); // 压入栈中
PREDICT(FOR_ITER); // 预测下一条指令
continue;
}
PyObject_GetIter(PyObject *o) {
PyTypeObject *t = o->ob_type; // 指向该对象的类型对象
getiterfunc f = NULL; // 获取迭代器的函数指针
if (PyType_HasFeature(t, Py_TPFLAGS_HAVE_ITER))
f = t->tp_iter; // 指向该类型的迭代器函数
PyObject *res = (*f)(o); // 获取对象
return res;
}
Python中迭代器通过对象来实现,迭代器对象也有对应的迭代器类型。
以list对象为例,t指向PyListObject的类型PyList_Type,f指向PyList_Type中的list_iter函数。list_iter函数创建迭代器对象listiterobject it[1],it的类型为PyListIter_Type,PyListIter_Type仅实现了和iter相关的函数。
list_iter(PyObject *seq)
{
listiterobject *it; // [1]
it = PyObject_GC_New(listiterobject, &PyListIter_Type);
it->it_index = 0; // 设置it
it->it_seq = (PyListObject *)seq;
_PyObject_GC_TRACK(it);
return (PyObject *)it; // 返回it
}
typedef struct {
PyObject_HEAD
long it_index;
PyListObject *it_seq; /* Set to NULL when iterator is exhausted */
} listiterobject;
所以,GET_ITER通过某对象PyObject找到其对用类型PyXXX_Type,通过PyXXX_Type的tp_iter函数找到并创建对应类型的迭代器xxxiterobject,通过xxxiterobject指向的迭代器类型PyXXXIter_Type中的相关iter函数进行操作。
FOR_ITER,进行循环。有上述内容就比较容易理解了,最终找到相关迭代器类型的tp_iternext,返回对应对象,同时会将it_index加1,从而实现迭代器指向下一个元素。将返回的对象压入栈中,并进行预测工作,接着执行下面的打印指令。如果返回空,则直接跳转到POP_BLOCK。
case FOR_ITER:
v = TOP();
x = (*v->ob_type->tp_iternext)(v); // [1]
if (x != NULL) {
PUSH(x); // [2]
PREDICT(STORE_FAST); // [3]
PREDICT(UNPACK_SEQUENCE);
continue;
}
x = v = POP();
Py_DECREF(v);
JUMPBY(oparg); // [4]
continue;
POP_BLOCK 其实是将PyTryBlock归还给f_iblock,并将运行时栈恢复为迭代前的状态。
3、WHILE循环
while循环便很好理解了。循环初始化[1],进行比较操作[2],达到要求则执行输出[3],输出跳转到比较操作,如果不能达到要求,跳转[5]到POP_BLOCK。
i = 0
while i < 10:
print(i)
i += 1
const : (0, 10, 1, None)
name : ('i', 'print')
1 0 LOAD_CONST 0 (0)
3 STORE_NAME 0 (i)
2 6 SETUP_LOOP 36 (to 45) // [1]
>> 9 LOAD_NAME 0 (i) // [2]
12 LOAD_CONST 1 (10)
15 COMPARE_OP 0 (<)
18 POP_JUMP_IF_FALSE 44 // [5]
3 21 LOAD_NAME 1 (print) // [3]
24 LOAD_NAME 0 (i)
27 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
30 POP_TOP
4 31 LOAD_NAME 0 (i)
34 LOAD_CONST 2 (1)
37 INPLACE_ADD
38 STORE_NAME 0 (i)
41 JUMP_ABSOLUTE 9 // [4]
>> 44 POP_BLOCK
>> 45 LOAD_CONST 3 (None)
48 RETURN_VALUE
continue,实现很简单,直接通过JUMP_ABSOLUTE跳转到下一轮判断处[2]。
break,其实是直接将运行状态恢复到循环之后的语句,即SETUP_LOOP传入参数处。
case BREAK_LOOP:
why = WHY_BREAK;
goto fast_block_end;
fast_block_end:
if (b->b_type == SETUP_LOOP && why == WHY_BREAK) {
why = WHY_NOT;
JUMPTO(b->b_handler);
4、异常控制流
很重要,很复杂,还没看懂……