编译自:http://www.aosabook.org/en/500L/a-python-interpreter-written-in-python.html 作者:Taavi Burns 翻译:鸿 如有翻译问题或建议,请公众号留言
真正的Python字节码 现在让进入真正的Python字节码中,从一个简单的函数开始:
>>> def cond():
... x = 3
... if x < 5:
... return 'yes'
... else:
... return 'no'
...
Python会在运行时暴露其内部机制,使得可以从REPL中直接获得这些东西。对于函数cond,cond.__ code__是cond的代码对象,而cond.__code__.co_code是其对应的字节码。平时在编写Python代码时,这些属性几乎没有任何用处,但是现在就能发挥用途了。
>>> cond.__code__.co_code # the bytecode as raw bytes
b'd\x01\x00}\x00\x00|\x00\x00d\x02\x00k\x00\x00r\x16\x00d\x03\x00Sd\x04\x00Sd\x00
\x00S'
>>> list(cond.__code__.co_code) # the bytecode as numbers
[100, 1, 0, 125, 0, 0, 124, 0, 0, 100, 2, 0, 107, 0, 0, 114, 22, 0, 100, 3, 0, 83,
100, 4, 0, 83, 100, 0, 0, 83]
仅仅只是print字节码,获得的就只是一系列字节信息。通过使用Python标准库中的dis模块(dis是一个字节码反汇编工具,可以翻译低级代码,例如汇编代码或字节码,并以人类可读的方式进行打印出来。),运行dis.dis时,就会给出cond函数传递的字节码的解释。
>>> dis.dis(cond)
2 0 LOAD_CONST 1 (3)
3 STORE_FAST 0 (x)
3 6 LOAD_FAST 0 (x)
9 LOAD_CONST 2 (5)
12 COMPARE_OP 0 (<)
15 POP_JUMP_IF_FALSE 22
4 18 LOAD_CONST 3 ('yes')
21 RETURN_VALUE
6 >> 22 LOAD_CONST 4 ('no')
25 RETURN_VALUE
26 LOAD_CONST 0 (None)
29 RETURN_VALUE
这是什么意思呢? 以第一条指令LOAD_CONST为例,第一列中的2给出的是Python源代码中的行号。第二列是字节码的索引,表示LOAD_CONST指令出现在零位。 第三列是指令本身,映射成人类可读的名称。第四列是该指令的参数。第五列表示这个参数的含义。继续再阅读这个字节码的前几个字节:[100,1,0,125,0,0]。这六个字节表示带参数的两条指令。可以使用dis.opname(将字节变成可理解字符串)来找出指令100和125对应的内容:
>>> dis.opname[100]
'LOAD_CONST'
>>> dis.opname[125]
'STORE_FAST'
第二个和第三个字节1,0是LOAD_CONST的参数,而第五个和第六个字节0,0是STORE_FAST的参数。就像前面的一样,LOAD_CONST需要知道在哪里找到要加载的常量,STORE_FAST需要找到存储的名称。(Python的LOAD_CONST与之前玩具解释器的LOAD_VALUE相同,LOAD_FAST与LOAD_NAME功能相同。)因此,这六个字节代表第一行代码,x = 3。(为什么每个参数需要使用两个字节?如果Python只使用一个字节来定位常量和名字,那么就只能有256个名字/常量与单个代码对象相关联,而使用两个字节,最多可以有256的平方,即65,536个)。