我想了解如何使用dis (the dissembler of Python bytecode)。具体来说,如何解释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
等是字节码指令(虽然有趣的是,BUILD_SET
没有出现在此列表中,但我希望它可以作为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
答案 0 :(得分:76)
您正在尝试反汇编包含源代码的字符串,但Python 2中的dis.dis
不支持该字符串。对于字符串参数,它将字符串视为包含字节代码(请参阅函数{{3 }})。因此,您会看到基于将源代码误解为字节代码的无意义输出。
Python 3中的情况有所不同,其中disassemble_string
in dis.py
在反汇编之前:
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
:
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处,我们找到65
,LOAD_NAME
的操作码,oparg 0000
;然后(在偏移3处)6a
是操作码LOAD_ATTR
,其中0100
是oparg,依此类推。请注意,opargs采用little-endian顺序,因此0100
是数字1.未记录的opcode
模块包含表opname
,为您提供每个操作码的名称,{{1给你每个名字的操作码:
opmap
oparg的含义取决于操作码,对于完整的故事,您需要阅读CPython虚拟机dis.dis
compiles a string argument的实现。对于>>> opcode.opname[0x65]
'LOAD_NAME'
和LOAD_NAME
,oparg是代码对象的LOAD_ATTR
属性的索引:
co_names
对于>>> co.co_names
('heapq', 'nlargest', 'd')
,它是代码对象的LOAD_CONST
属性的索引:
co_consts
对于>>> co.co_consts
(3,)
,它是传递给函数的参数个数,以16位编码,低字节中的普通参数数量,以及高字节中的关键字参数数量。
答案 1 :(得分:28)
我将答案重新发送到another question,以确保在Google搜索dis.dis()
时找到它。
要完成伟大的Gareth Rees's answer,这里只是一个小的逐列摘要来解释反汇编字节码的输出。
例如,给定此功能:
def f(num):
if num == 42:
return True
return False
这可能会被反汇编成(Python 3.6):
(1)|(2)|(3)|(4)| (5) |(6)| (7)
---|---|---|---|----------------------|---|-------
2| | | 0|LOAD_FAST | 0|(num)
|-->| | 2|LOAD_CONST | 1|(42)
| | | 4|COMPARE_OP | 2|(==)
| | | 6|POP_JUMP_IF_FALSE | 12|
| | | | | |
3| | | 8|LOAD_CONST | 2|(True)
| | | 10|RETURN_VALUE | |
| | | | | |
4| |>> | 12|LOAD_CONST | 3|(False)
| | | 14|RETURN_VALUE | |
每列都有特定目的:
JUMP
从之前的说明到此dis
module中简要解释,它们的实现可以在ceval.c
(CPython的核心循环)< / LI>