我应该如何理解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处,我们find65
, LOAD_NAME
的操作码,oparg为0000
; 那么(在偏移3处) 6a
是操作码LOAD_ATTR
, 0100
是oparg,依此类推。 请注意,opargs是小端顺序,所以0100
是数字1.未opname
opcode
模块包含表opname
给你每个操作码的名称, opmap
给你每个名称的操作码:
>>> opcode.opname[0x65] 'LOAD_NAME'
oparg的含义取决于操作码,对于完整的故事,您需要阅读ceval.c
CPython虚拟机的ceval.c
。 对于LOAD_NAME
和LOAD_ATTR
,oparg是代码对象的co_names属性的索引:
>>> co.co_names ('heapq', 'nlargest', 'd')
对于LOAD_CONST
它是代码对象的co_consts
属性的索引:
>>> co.co_consts (3,)
对于CALL_FUNCTION
,传递给函数的参数数量是16位,低位字节中的普通参数个数,高位字节中的关键字参数个数。