一个简单的例子

我们从一个简单的python代码入手,逐步窥探python的运行机制。

我们创建一个add.py文件,填入如下内容

# Create a File `add.py`
a = 1
b = 2
r = a+b
print(r)

如何产生字节码?

Python本身提供了方便的编译和汇编查询方法。

我们在shell中启动Python依次执行如下Python代码

# get a code object
co = compile(open('add.py').read(), 'add.py', 'exec')

print(co)
#<code object <module> at 0x1065ad030, file "add.py", line 1>

# show the byte code
print(co.co_code)
print(co.co_names)

help(compile) 
dir(co)

通过Python自带的compile()函数可以获取代码编译后产生的code object,其中可以看到编译后产生的字节码co.co_code和变量名 co.co_names 这就是上文提及的代码编译之后产生的字节码,Python用一个objec存储它,并且除了存储字节码本身外,还存储了他的其他相关信息。

如何查看字节码对应的指令?

Python提供了dis模块用来做反汇编,我们终端中执行以下python

python -m dis add.py

会得到以下的python汇编

  1           0 LOAD_CONST               0 (1)
              2 STORE_NAME               0 (a)

  2           4 LOAD_CONST               1 (2)
              6 STORE_NAME               1 (b)

  3           8 LOAD_NAME                0 (a)
             10 LOAD_NAME                1 (b)
             12 BINARY_ADD
             14 STORE_NAME               2 (c)

  4          16 LOAD_NAME                3 (print)
             18 LOAD_NAME                2 (c)
             20 CALL_FUNCTION            1
             22 POP_TOP
             24 LOAD_CONST               2 (None)
             26 RETURN_VALUE

啊哈! 这就是Python编译所做的工作,把Python代码变成了这样的一条条指令,而Interpreter只需要从第一行开始,一行一行的执行就可以了。如果汇编基础比较好的同学应该可以通过指令名称推测出其中的含义。逐条的大致作用如下:

  1. 我们先载入一个值为1的对象
  2. 把它存在name为a的对象里
  3. 类似的,载入2
  4. 存在name为b的对象中
  5. 加载对象a
  6. 加载对象b
  7. 对刚刚加载的两个对象执行BINARY_ADD加法运算
  8. 把运算结果存到对象c中
  9. 加载名字叫print的对象
  10. 加载对象c
  11. 执行call function指令
  12. ...

那么具体看,Python的Interpreter又是如何逐条执行这些指令的呢?

results matching ""

    No results matching ""