Bdd52225ca2fc1d722d4e9e06dac4254
Python的字节码

Python的字节码

在上文《Python的编译:从py到pyc》中,我们提到Python实际上会先将你编写的程序编译成平台无关的字节码,今天我们就来深入探讨这个字节码。

字节码种类

以我们当前研究的Python 2.7.x来探讨,所有的字节码都位于include/opcode.h,为了考虑向下兼容等特性,设计了大约不足147个种类的字节码。

说是字节码,其实和我们平常了解到的汇编助记符也很类似,一般都是都是以操作符 + 操作数的形式来组合,然后再根据操作数的不同来区分二元操作符、三元操作符等。

这里我们以最基本的加法来简单举个例子:

>>> def test(a, b):
...     c = a + b
...     return c

我们通过内置的dis反编译模块,可以获取到对应的字节码:

  2           0 LOAD_FAST                0 (a)
              3 LOAD_FAST                1 (b)
              6 BINARY_ADD          
              7 STORE_FAST               2 (c)

  3          10 LOAD_FAST                2 (c)
             13 RETURN_VALUE        

我们关注下BINARY_ADD,这条字节码背后的含义就是执行二元的加法,而在这之前,需要准备该操作符的两个操作数,这也是前两行LOAD_FAST的作用。

如果你对与二进制协议比较理解,你可能会想更深入的研究下字节码是如何从二进制流中解析获得,这个时候你可以尝试使用test.__code__.co_code,会得到如下输出:

'|\x00\x00|\x01\x00\x17}\x02\x00|\x02\x00S'

是不是看起来一点懵逼?我们来把它拆分一下:

'|', '\x00', '\x00', '|', '\x01', '\x00', '\x17', '}', '\x02', '\x00', '|', '\x02', '\x00', 'S'
  • 首先来看|,如果将其从字符串转换成整数,利用ord('|'),会得到结果124,而这正好是对应的字节码LOAD_FAST
  • 后面紧跟的'\x00',代表的是索引0位置。
  • 再后面又来的一个'\x00',代表的当前字节码解析结束。
  • '\x17',转换成十进制的数字就是23,就是对应的BINARY_ADD
top Created with Sketch.