我应该如何理解dis.dis的输出?

我想了解如何使用DIS(Python字节码的拆解者) 。 具体来说,如何解释dis.dis (或dis.disassemble )的输出?

这是一个非常具体的例子(在Python 2.7.3中):

 dis.dis("heapq.nsmallest(d,3)") 0 BUILD_SET 24933 3 JUMP_IF_TRUE_OR_POP 11889 6 JUMP_FORWARD 28019 (to 28028) 9 STORE_GLOBAL 27756 (27756) 12 LOAD_NAME 29811 (29811) 15 STORE_SLICE+0 16 LOAD_CONST 13100 (13100) 19 STORE_SLICE+1 

我看到JUMP_IF_TRUE_OR_POP等是字节码指令(尽pipe有趣的是, BUILD_SET没有出现在这个列表中,但我期望它可以像BUILD_TUPLE一样BUILD_TUPLE 。 我认为右边的数字是内存分配,左边的数字是goto数字…我注意到他们每次几乎增加3(但不完全)。

如果我在函数中包装dis.dis("heapq.nsmallest(d,3)")

 def f_heapq_nsmallest(d,n): return heapq.nsmallest(d,n) dis.dis("f_heapq(d,3)") 0 BUILD_TUPLE 26719 3 LOAD_NAME 28769 (28769) 6 JUMP_ABSOLUTE 25640 9 <44> # what is <44> ? 10 DELETE_SLICE+1 11 STORE_SLICE+1 

您正在尝试反汇编包含源代码的string,但Python 2中的dis.dis不支持该string。对于string参数,它将string视为包含字节码(请参阅dis.py中的dis.py函数)。 所以你看到基于把源代码误解为字节码的无意义的输出。

在Python 3中,情况是不同的, dis.dis在反汇编之前编译了一个string参数 :

 Python 3.2.3 (default, Aug 13 2012, 22:28:10) >>> import dis >>> dis.dis('heapq.nlargest(d,3)') 1 0 LOAD_NAME 0 (heapq) 3 LOAD_ATTR 1 (nlargest) 6 LOAD_NAME 2 (d) 9 LOAD_CONST 0 (3) 12 CALL_FUNCTION 2 15 RETURN_VALUE 

在Python 2中,您需要在将代码传递给dis.dis之前dis.dis编译代码:

 Python 2.7.3 (default, Aug 13 2012, 18:25:43) >>> import dis >>> dis.dis(compile('heapq.nlargest(d,3)', '<none>', 'eval')) 1 0 LOAD_NAME 0 (heapq) 3 LOAD_ATTR 1 (nlargest) 6 LOAD_NAME 2 (d) 9 LOAD_CONST 0 (3) 12 CALL_FUNCTION 2 15 RETURN_VALUE 

这些数字代表着什么? 最左边的数字1是编译该字节码的源代码中的行号。 左边列中的数字是字节码内指令的偏移量,右边的数字是opargs 。 我们来看看实际的字节码:

 >>> co = compile('heapq.nlargest(d,3)', '<none>', 'eval') >>> co.co_code.encode('hex') '6500006a010065020064000083020053' 

在字节码的偏移量0处,我们find65LOAD_NAME的操作码,oparg为0000 ; 那么(在偏移3处) 6a是操作码LOAD_ATTR0100是oparg,依此类推。 请注意,opargs是小端顺序,所以0100是数字1.未opname opcode模块包含表opname给你每个操作码的名称, opmap给你每个名称的操作码:

 >>> opcode.opname[0x65] 'LOAD_NAME' 

oparg的含义取决于操作码,对于完整的故事,您需要阅读ceval.c CPython虚拟机的ceval.c 。 对于LOAD_NAMELOAD_ATTR ,oparg是代码对象的co_names属性的索引:

 >>> co.co_names ('heapq', 'nlargest', 'd') 

对于LOAD_CONST它是代码对象的co_consts属性的索引:

 >>> co.co_consts (3,) 

对于CALL_FUNCTION ,传递给函数的参数数量是16位,低位字节中的普通参数个数,高位字节中的关键字参数个数。