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

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_stringdis.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_NAMELOAD_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位编码,低字节中的普通参数数量,以及高字节中的关键字参数数量.

  • @DevC:使用[`inspect`](http://docs.python.org/3/library/inspect.html)模块记录代码对象.字节代码指令记录在[`dis`](http://docs.python.org/3/library/dis.html#bytecodes)模块中.有关CPython虚拟机的实现细节,您必须阅读[`ceval.c`](http://hg.python.org/cpython/file/47618b00405b/Python/ceval.c)中的源代码. (3认同)
  • 太棒了,有没有任何参考/教程/书籍具有所有这些低级细节.我想挖掘更多 (2认同)

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)

每列都有特定目的:

  1. 源代码中对应的行号
  2. (可选)指示当前执行的指令(例如,当字节码来自帧对象时)
  3. 一个标签,表示可能JUMP从之前的指令到此
  4. 地址对应于字节索引在字节码(这些都是2因为Python 3.6使用2个字节用于每个指令的倍数,而它可以在先前的版本而变化)
  5. 指令名称(也称为opname),每一个都是在简要介绍dis模块及其实施中可以找到ceval.c(CPython的核心环)
  6. Python内部用于获取某些常量或变量,管理堆栈,跳转到特定指令等的指令的参数(如果有).
  7. 教学论证的人性化解释

  • 这就是我要寻找的!但是我在官方文档中找不到 (2认同)