对于一下代码
i = 1
s = 'python'
d = {}
l = []
解析编译后的pyc文件得到如下结果:
从字节码查看consts名字空间与names名字空间,得到以下结果:
在Python程序运行开始前,运行时栈与locals命名空间像下图所示那样:
当执行完"i=1"这条指令,字节码为:
LOAD_CONST指令源码如下:
表示从consts中读取对应序号的元素,压入运行时栈中。
当执行字节码"0 LOAD_CONST 0 (1)"时,运行时栈与locals名字空间变成了下边这样:
表示取consts中编号为0的元素(connsts中的元素见上边图片从字节码查看consts名字空间与names名字空间),然后将其压入运行时栈中。
STORE_NAME指令源码如下:
表示从符号中(names)中获取符号,然后从运行时栈中获取栈顶元素,再将(符号,值)得映射关系存到locals 名字空间中。
当执行字节码"2 STORE_NAME 0 (i)"时,运行时栈与locals名字空间变成了下边这样:
因为在STORE_NAME指令执行得过程中,执行了pop的动作,所以运行时栈为空了。
在执行第二句命令"s = 'python'",字节码为:
操作与第一句指令一样,只是值变了而已,所以运行时栈与locals名字空间的变化如下:
在执行第三句指令"d = {}",字节码为:
BUILD_MAP源码为:
表明创建一个字典,然后压入栈中(ps: 字节码指令中的参数0 并没有用到)。
所以当'd={}'的指令执行结束时,运行时栈与locals名字空间的变化如下:
对于第四句语句"l = []",字节码为:
对于这句指令,竟然编译出四条字节码指令。
BUILD_LIST源码为:
与BUILD_MAP类似,BUILD_LIST创建一个列表,但是会用到字节码中所带的指令参数,因为在创建列表的时候不一定是一个空的list, 那么在BUILD_LIST指令之前一定也会有许多LOAD_CONST的操作,先把列表中的元素压入运行时栈,在真正执行BUILD_LIST指令时,会将这些对象一一从栈中弹出,加入到新建的PyListObject对象中。
在执行“ 14 STORE_NAME 3 (l)”指令之后,似乎创建列表的工作完成了,也就是整个测试程序都执行完了,那么为什么会还有两句字节码呢?
原来Python在执行了一段Code Block后,一定要返回一些值,这两条字节码指令就是用来返回某些值得:
RETURN_VALUE指令源码如下:
从源码可以看出,实际得返回值retval是从运行时栈中获得到。所以RETURN_VALUE之前的那句“16 LOAD_CONST 2 (None)”命令的作用就不言而喻了。就是将返回值压入运行时栈中,以供RETURN_VALUE指令使用。可以看到压入栈中的十一个NoneObject,实际是没什么意义的东西,但这个过程还是要走。
最终运行时栈与locals名字空间变成了如下这样:
运行时栈已经边空,所有有用的信息都已经加载到locals名字空间的掌控之中。