编译脚本的字节代码根据编译方式而有所不同

cs9*_*s95 9 python bytecode disassembly pyc python-internals

当天早些时候,我正在大量尝试使用docstrings和dis模块,并且遇到了一些我似乎找不到答案的东西.

首先,我创建一个test.py包含以下内容的文件:

def foo():
    pass
Run Code Online (Sandbox Code Playgroud)

就是这样,没有别的.

然后我打开一个解释器来观察程序的字节码.你可以这样得到它:

code = compile(open('test.py').read(), '', 'exec')
Run Code Online (Sandbox Code Playgroud)

第一个参数是字符串形式的代码,第二个参数用于调试目的(将其留空即可),而第三个参数是模式.我都试过singleexec.结果是一样的.

在此之后,您可以使用反编译字节码dis.

>>> import dis
>>> dis.dis(code)
Run Code Online (Sandbox Code Playgroud)

字节码输出是这样的:

 1           0 LOAD_CONST               0 (<code object foo at 0x10a25e8b0, file "", line 1>)
              3 MAKE_FUNCTION            0
              6 STORE_NAME               0 (foo)
              9 LOAD_CONST               1 (None)
             12 RETURN_VALUE        
Run Code Online (Sandbox Code Playgroud)

合理的,对于这样一个简单的脚本.它也是有道理的.

然后我尝试通过命令行编译它,如下所示:

$ python -m py_compile test.py
Run Code Online (Sandbox Code Playgroud)

这导致生成字节码并将其放置在test.pyc文件中.内容可以再次拆卸:

>>> import dis
>>> dis.dis(open('test.pyc').read())
Run Code Online (Sandbox Code Playgroud)

这是输出:

>>    0 ROT_THREE      
      1 <243>            2573
>>    4 <157>           19800
>>    7 BUILD_CLASS    
      8 DUP_TOPX            0
     11 STOP_CODE      
     12 STOP_CODE      
>>   13 STOP_CODE      
     14 STOP_CODE      
     15 STOP_CODE      
     16 STOP_CODE      
     17 POP_TOP        
     18 STOP_CODE      
     19 STOP_CODE      
     20 STOP_CODE      
     21 BINARY_AND     
     22 STOP_CODE      
     23 STOP_CODE      
     24 STOP_CODE      
     25 POP_JUMP_IF_TRUE    13
     28 STOP_CODE      
     29 STOP_CODE      
     30 LOAD_CONST          0 (0)
     33 MAKE_FUNCTION       0
     36 STORE_NAME          0 (0)
     39 LOAD_CONST          1 (1)
     42 RETURN_VALUE   
     43 STORE_SLICE+0  
     44 ROT_TWO        
     45 STOP_CODE      
     46 STOP_CODE      
     47 STOP_CODE      
     48 DUP_TOPX            0
     51 STOP_CODE      
     52 STOP_CODE      
     53 STOP_CODE      
     54 STOP_CODE      
     55 STOP_CODE      
     56 STOP_CODE      
     57 POP_TOP        
     58 STOP_CODE      
     59 STOP_CODE      
     60 STOP_CODE      
     61 INPLACE_POWER  
     62 STOP_CODE      
     63 STOP_CODE      
     64 STOP_CODE      
     65 POP_JUMP_IF_TRUE     4
     68 STOP_CODE      
     69 STOP_CODE      
     70 LOAD_CONST          0 (0)
     73 RETURN_VALUE   
     74 STORE_SLICE+0  
     75 POP_TOP        
     76 STOP_CODE      
     77 STOP_CODE      
     78 STOP_CODE      
     79 INPLACE_XOR    
     80 STORE_SLICE+0  
     81 STOP_CODE      
     82 STOP_CODE      
     83 STOP_CODE      
     84 STOP_CODE      
     85 STORE_SLICE+0  
     86 STOP_CODE      
     87 STOP_CODE      
     88 STOP_CODE      
     89 STOP_CODE      
     90 STORE_SLICE+0  
     91 STOP_CODE      
     92 STOP_CODE      
     93 STOP_CODE      
     94 STOP_CODE      
     95 STORE_SLICE+0  
     96 STOP_CODE      
     97 STOP_CODE      
     98 STOP_CODE      
     99 STOP_CODE      
    100 POP_JUMP_IF_TRUE     7
    103 STOP_CODE      
    104 STOP_CODE      
    105 LOAD_GLOBAL     29541 (29541)
    108 LOAD_GLOBAL     28718 (28718)
    111 SETUP_EXCEPT      884 (to 998)
    114 STOP_CODE      
    115 STOP_CODE      
    116 STOP_CODE      
    117 BUILD_TUPLE     28527
    120 POP_TOP        
    121 STOP_CODE      
    122 STOP_CODE      
    123 STOP_CODE      
    124 POP_JUMP_IF_TRUE     2
    127 STOP_CODE      
    128 STOP_CODE      
    129 STOP_CODE      
    130 POP_TOP        
    131 INPLACE_XOR    
    132 STORE_SLICE+0  
    133 POP_TOP        
    134 STOP_CODE      
    135 STOP_CODE      
    136 STOP_CODE      
    137 LOAD_LOCALS    
    138 STOP_CODE      
    139 STOP_CODE      
    140 STOP_CODE      
    141 STOP_CODE      
    142 STORE_SLICE+0  
    143 STOP_CODE      
    144 STOP_CODE      
    145 STOP_CODE      
    146 STOP_CODE      
    147 STORE_SLICE+0  
    148 STOP_CODE      
    149 STOP_CODE      
    150 STOP_CODE      
    151 STOP_CODE      
    152 STORE_SLICE+0  
    153 STOP_CODE      
    154 STOP_CODE      
    155 STOP_CODE      
    156 STOP_CODE      
    157 POP_JUMP_IF_TRUE     7
    160 STOP_CODE      
    161 STOP_CODE      
    162 LOAD_GLOBAL     29541 (29541)
    165 LOAD_GLOBAL     28718 (28718)
    168 SETUP_EXCEPT     2164 (to 2335)
    171 STOP_CODE      
    172 STOP_CODE      
    173 STOP_CODE      
    174 STORE_SUBSCR   
    175 IMPORT_FROM     25711 (25711)
    178 <117>           25964
    181 BINARY_LSHIFT  
    182 POP_TOP        
    183 STOP_CODE      
    184 STOP_CODE      
    185 STOP_CODE      
    186 POP_JUMP_IF_TRUE     0
    189 STOP_CODE      
    190 STOP_CODE      
Run Code Online (Sandbox Code Playgroud)

差异是惊人的.为什么字节码中存在如此明显的对比,具体取决于它是如何编译的?

use*_*ica 15

.pyc文件的内容不是原始Python字节码指令.一个.pyc文件包含

  1. 一个4字节的幻数,
  2. 一个4字节的修改时间戳,和
  3. 一个编组代码对象.

你基本上只是第二次拆卸垃圾.

如果要从a中反汇编代码.pyc,可以跳过8个字节,解组代码对象,然后调用dis.dis代码对象:

import dis
import marshal

with open('test.pyc', 'b') as f:
    f.seek(8)
    dis.dis(marshal.load(f))
Run Code Online (Sandbox Code Playgroud)

请注意,.pyc格式可以在不同版本之间自由更改,因此这可能并不总是有效.事实上,从引用文章开始,它已经发生了变化; 他们在Python 3.3中的源文件大小的时间戳之后添加了4个字节,因此在3.3及更高版本中,您必须跳过12个字节.

  • @Coldspeed:这是合法的,因为你不熟悉正常的字节码.例如,`STOP_CODE`不出现在正常的字节码中.至于`py_compile`的作用,它编译Python文件,但它将它们编译为`.pyc`文件而不是代码对象. (3认同)