Python虚拟机中的一般表达式(二)
复杂内建对象的创建
在上一章Python虚拟机中的一般表达式(一)中,我们看到了Python是如何创建一个空的字典对象和列表对象,那么如果创建一个非空的字典对象和列表对象,Python的行为又是如何呢?demo2.py里面包含一个字典对象和列表对象,这两个对象都是在初始化时就包含元素,首先,我们看一下对应PyCodeObject中的符号表和常量表
# cat demo2.py i = 1 s = "Python" d = {"1": 1, "2": 2} l = [1, 2] # python2.5 …… >>> source = open("demo2.py").read() >>> co = compile(source, "demo2.py", "exec") >>> co.co_names ('i', 's', 'd', 'l') >>> co.co_consts (1, 'Python', '1', 2, '2', None)
其次,我们再用dis模块解释一下demo2.py所对应的字节码
>>> import dis >>> dis.dis(co) 1 0 LOAD_CONST 0 (1) 3 STORE_NAME 0 (i) 2 6 LOAD_CONST 1 ('Python') 9 STORE_NAME 1 (s) 3 12 BUILD_MAP 0 15 DUP_TOP 16 LOAD_CONST 0 (1) 19 ROT_TWO 20 LOAD_CONST 2 ('1') 23 STORE_SUBSCR 24 DUP_TOP 25 LOAD_CONST 3 (2) 28 ROT_TWO 29 LOAD_CONST 4 ('2') 32 STORE_SUBSCR 33 STORE_NAME 2 (d) 4 36 LOAD_CONST 0 (1) 39 LOAD_CONST 3 (2) 42 BUILD_LIST 2 45 STORE_NAME 3 (l) 48 LOAD_CONST 5 (None) 51 RETURN_VALUE
现在,我们来分析一下Python虚拟机是如何创建包含元素的字典对象和列表对象
首先是字典对象:
d = {"1": 1, "2": 2} //分析结果 1 12 BUILD_MAP 0 15 DUP_TOP 16 LOAD_CONST 0 (1) 19 ROT_TWO 20 LOAD_CONST 2 ('1') 23 STORE_SUBSCR 24 DUP_TOP 25 LOAD_CONST 3 (2) 28 ROT_TWO 29 LOAD_CONST 4 ('2') 32 STORE_SUBSCR 33 STORE_NAME 2 (d)
BUILD_MAP会创建一个空的字典,并压入运行时栈,这没什么可说的,我们看下BUILD_MAP之后的指令DUP_TOP
ceval.c
case DUP_TOP: v = TOP(); Py_INCREF(v); PUSH(v); goto fast_next_opcode;
DUP_TOP会获取栈顶的元素,增加其引用,又再一次将栈顶元素压入栈中,紧接着LOAD_CONST指令又会将1这个对象压入到运行时栈,那么我们来看下前3条指令执行完毕后运行时栈和名字空间的分布:
图1-1
Python虚拟机在接下来又执行指令ROT_TWO,我们再来看一下关于这条指令所做的内容:
ceval.c
case ROT_TWO: v = TOP(); w = SECOND(); SET_TOP(w); SET_SECOND(v); goto fast_next_opcode;
ROT_TWO也是调用其他宏来完成任务的,我们看一下这几条宏的指令:
ceval.c
#define TOP() (stack_pointer[-1]) #define SECOND() (stack_pointer[-2]) #define SET_TOP(v) (stack_pointer[-1] = (v)) #define SET_SECOND(v) (stack_pointer[-2] = (v))
原来ROT_TWO这条指令会取出运行时栈的第一个元素和第二个元素,然后将其位置对调,执行完ROT_TWO之后,我们再来看一下运行时栈和名字空间的分布:
图1-2
随后又执行了一条LOAD_CONST指令,将字符串'1'压入到运行时栈中,之后,又执行了STORE_SUBSCR这条指令,而正是这条指令将字符串"1"和整数值1之间的映射建立在之前的字典对象上,我们来看一下STORE_SUBSCR的内容:
case STORE_SUBSCR: w = TOP(); v = SECOND(); u = THIRD(); STACKADJ(-3); /* v[w] = u */ err = PyObject_SetItem(v, w, u); Py_DECREF(u); Py_DECREF(v); Py_DECREF(w); if (err == 0) continue; break;
w是之前压入栈中的字符串"1",而v是运行时栈的自栈顶开始的第二个元素,是字典对象,而u则是最早之前压入栈中的值,即为整数值1,通过PyObject_SetItem(v, w, u)建立字符串"1"和整数值1在字典上的映射关系。同理字符串"2"和整数值2也是基于一样的字节码建立的映射关系,最后执行STORE_NAME字节码,在名字空间上建立符号d和字典对象的映射
在成功创建字典对象后并赋予初值,还会创建列表对象,列表对象会先将初值用LOAD_CONST压入运行时栈,再调用BUILD_LIST时将栈中的元素一个个压入列表对象,BUILD_LIST的内容在上一章Python虚拟机中的一般表达式(一)中已经解释,这里不再重复
l = [1, 2] //分析结果 4 36 LOAD_CONST 0 (1) 39 LOAD_CONST 3 (2) 42 BUILD_LIST 2 45 STORE_NAME 3 (l)