And*_*den 62 python python-2.7
我想了解如何使用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
Run Code Online (Sandbox Code Playgroud)
我看到JUMP_IF_TRUE_OR_POP等等是字节码指令(虽然有趣的是,BUILD_SET这个列表中没有出现,但我希望它可以工作BUILD_TUPLE).我认为右边的数字是内存分配,左边的数字是转到数字......我注意到它们每次几乎增加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
Run Code Online (Sandbox Code Playgroud)
Gar*_*ees 81
您正在尝试拆解内含源代码的字符串,但是这不支持dis.dis在Python 2.用字符串参数,它把字符串,如果它包含字节码(见函数disassemble_string中dis.py).因此,您会看到基于将源代码误解为字节代码的无意义输出.
Python 3中的情况有所不同,其中在反汇编之前dis.dis编译字符串参数:
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
Run Code Online (Sandbox Code Playgroud)
在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
Run Code Online (Sandbox Code Playgroud)
这些数字是什么意思?数1在最左边是在从其中该字节代码被编译的源代码的行号.左边列中的数字是字节码中指令的偏移量,右边的数字是opargs.我们来看看实际的字节代码:
>>> co = compile('heapq.nlargest(d,3)', '<none>', 'eval')
>>> co.co_code.encode('hex')
'6500006a010065020064000083020053'
Run Code Online (Sandbox Code Playgroud)
在我们找到的字节代码中的偏移0处,使用oparg 65的操作码; 然后(在偏移量3处)是操作码,使用oparg,依此类推.请注意,opargs是小端序,因此是数字1.未记录的模块包含表,为您提供每个操作码的名称,并为您提供每个名称的操作码:LOAD_NAME00006aLOAD_ATTR01000100opcodeopnameopmap
>>> opcode.opname[0x65]
'LOAD_NAME'
Run Code Online (Sandbox Code Playgroud)
oparg的含义取决于操作码,对于完整的故事,您需要阅读CPython虚拟机的实现ceval.c.For LOAD_NAME和LOAD_ATTRoparg是co_names代码对象属性的索引:
>>> co.co_names
('heapq', 'nlargest', 'd')
Run Code Online (Sandbox Code Playgroud)
因为LOAD_CONST它是co_consts代码对象属性的索引:
>>> co.co_consts
(3,)
Run Code Online (Sandbox Code Playgroud)
因为CALL_FUNCTION,它是传递给函数的参数个数,以16位编码,低字节中的普通参数数量,以及高字节中的关键字参数数量.
Del*_*gan 36
我正在将我的答案转发给另一个问题,以确保在谷歌搜索时找到它dis.dis().
要完成伟大的Gareth Rees的答案,这里只是一个小的逐列摘要来解释反汇编字节码的输出.
例如,给定此功能:
def f(num):
if num == 42:
return True
return False
Run Code Online (Sandbox Code Playgroud)
这可能会被反汇编成(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 | |
Run Code Online (Sandbox Code Playgroud)
每列都有特定目的:
JUMP从之前的指令到此dis模块及其实施中可以找到ceval.c(CPython的核心环)| 归档时间: |
|
| 查看次数: |
10279 次 |
| 最近记录: |