source = open('test.py').read()
co = compile(source, 'test.py', 'exec')
print("const\t:\t", co.co_consts)
print("name\t:\t", co.co_names)
import dis
dis.dis(co)
测试程序test.py:
i = 1
s = "efei"
d = {}
l = []
执行co_dist可得结果:
const : (1, 'efei', None)
name : ('i', 's', 'd', 'l')
1 0 LOAD_CONST 0 (1)
3 STORE_NAME 0 (i)
2 6 LOAD_CONST 1 ('efei')
9 STORE_NAME 1 (s)
3 12 BUILD_MAP 0
15 STORE_NAME 2 (d)
4 18 BUILD_LIST 0
21 STORE_NAME 3 (l)
24 LOAD_CONST 2 (None)
27 RETURN_VALUE
可看到consts存储的对象,names存储的值,并且都是以PyTupleObject存储的。
1 0 LOAD_CONST 0 (1)
3 STORE_NAME 0 (i)
其中:
case LOAD_CONST:
x = GETITEM(consts, oparg);
Py_INCREF(x);
PUSH(x);
#define GETITEM(v, i) PyTuple_GetItem((v), (i))
oparg即为传入的参数0。所以LOAD_CONST 0 解释为:从consts中取出第0个对象x,将x压入运行栈中。
case STORE_NAME:
w = GETITEM(names, oparg);
v = POP();
if ((x = f->f_locals) != NULL) {
if (PyDict_CheckExact(x))
err = PyDict_SetItem(x, w, v);
else
err = PyObject_SetItem(x, w, v);
Py_DECREF(v);
if (err == 0) continue;
break;
}
STORE_NAME 0 的解释为:从名为names中取出第0个对象w,运行时栈中弹出v,将(w, v)放入_dict对象f_locals中,即局部变量。
2 6 LOAD_CONST 1 ('efei')
9 STORE_NAME 1 (s)
运行时的状态图:
3 12 BUILD_MAP 0
15 STORE_NAME 2 (d)
4 18 BUILD_LIST 0
21 STORE_NAME 3 (l)
24 LOAD_CONST 2 (None)
27 RETURN_VALUE
类似,只不过BUILD_MAP创建一个_dict对象,BUILD_LIST创建一个_list对象。
case BUILD_MAP:
x = _PyDict_NewPresized((Py_ssize_t)oparg);
PUSH(x);
case BUILD_LIST:
x = PyList_New(oparg);
if (x != NULL) {
for (; --oparg >= 0;) {
w = POP();
PyList_SET_ITEM(x, oparg, w);
}
PUSH(x);
BUILD_MAP 直接创建一个空对象,然后入栈;BUILD_LIST创建一个对象之后会将前面oparg栈中的对象弹出放入_list对象中,然后将_list对象入栈。
i = 1
s = "efei"
d = {"1":1, "2":2}
l = [1, 2]
const : (1, 'efei', '1', 2, '2', None)
name : ('i', 's', 'd', 'l')
1 0 LOAD_CONST 0 (1)
3 STORE_NAME 0 (i)
2 6 LOAD_CONST 1 ('efei')
9 STORE_NAME 1 (s)
3 12 BUILD_MAP 2 // 新建空_dict,并压入栈
15 LOAD_CONST 0 (1) // 将第一个元素的key压入栈
18 LOAD_CONST 2 ('1') // 将第一个元素的val压入栈
21 STORE_MAP // 将第一个元素放入_dict
22 LOAD_CONST 3 (2) // 第二个元素……
25 LOAD_CONST 4 ('2')
28 STORE_MAP
29 STORE_NAME 2 (d) // 将_dict放入f_locals
4 32 LOAD_CONST 0 (1) // 将list中的元素压入栈
35 LOAD_CONST 3 (2) // ……
38 BUILD_LIST 2 // 创建_list,并将栈中的前2个对象放入_list
41 STORE_NAME 3 (l) // 将_list放入f_locals
44 LOAD_CONST 5 (None)
47 RETURN_VALUE
如上,需注意 d = {"1":1, "2":2} 的汇编指令与《Python源码剖析》中不同,书中用的Python2.5,本处用的Python3.3,其实Python2.7.5也是上述指令。
case STORE_MAP:
w = TOP(); /* key */
u = SECOND(); /* value */
v = THIRD(); /* dict */
STACKADJ(-2);
assert (PyDict_CheckExact(v));
err = PyDict_SetItem(v, w, u); /* v[w] = u */
Py_DECREF(u);
Py_DECREF(w);
比Pyhotn2.5逻辑更清晰,汇编指令更清晰了,STORE_MAP的操纵也更清晰了。
a = 5
b = a
c = a + b
print(c)
const : (5, None)
name : ('a', 'b', 'c', 'print')
1 0 LOAD_CONST 0 (5)
3 STORE_NAME 0 (a)
2 6 LOAD_NAME 0 (a) // 在名字空间中搜索a,并将其值压入栈
9 STORE_NAME 1 (b)
3 12 LOAD_NAME 0 (a)
15 LOAD_NAME 1 (b)
18 BINARY_ADD // 相加,将结果压入栈
19 STORE_NAME 2 (c)
4 22 LOAD_NAME 3 (print) // 搜索并压入函数对象
25 LOAD_NAME 2 (c) // 搜索并压入C对应的对象
28 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
31 POP_TOP
32 LOAD_CONST 1 (None)
35 RETURN_VALUE
b = a 的汇编指令为 LOAD_NAME 与 STORE_NAME。LOAD_NAME实质为,依次在f_locals、f_globals、f_builtins中搜索,如果找到则将相应的对象压入栈中,找不到会终止解释器运行。(其实栈中存储的均为对象的指针……)
执行后,已经将具体值赋给了b,图示如下:为验证推测,添加语句print(a,b)汇编指令会添加
5 32 LOAD_NAME 3 (print)
35 LOAD_NAME 0 (a)
38 LOAD_NAME 1 (b)
41 CALL_FUNCTION 2 (2 positional, 0 keyword pair)
44 POP_TOP
和预测相符。