zenith-docs 1.0.0 Help

Python 解释器是如何工作的

这一篇文档将用于解释 Python 的解释器是如何工作的。在正式进入正文之前,我们需要先了解什么是解释器,以及和解释器相对的编译器。

解释器和编译器

什么是解释器?本质上它也是一个软件,当我们在运行 python -c "print('Hello')  这条命令的时候,实际上就是调用了一个名为 python 的解释器程序,去将 print('Hello') 这一句代码解释成机器码。

什么是机器码?可以说是机器指令,也就是我们的 CPU 唯一能够识别的指令。每一个 CPU 都有其出厂时候就设定好的指令集,包含了所有它能够识别的指令。也只有把我们的程序代码解释成这些指令,CPU 才能正确地执行。 机器码几乎是难以阅读和编写的,所以我们才有了更高级的语言语法,但这些语言语法需要被翻译成机器码,CPU 才能执行。** 那么什么是编译器呢? 当我们要安装 python 的解释器的时候,就需要使用 GCC 编译套件对 Python 的 C 语言编写的代码进行编译。那么 Gcc 就是一款编译器。它也是将代码翻译成机器码,然后程序才能运行。

那么编译器和解释器的区别是什么呢?都是做翻译工作,但是翻译的时机不一样。解释器在程序运行时进行翻译源代码工作的,而编译器是在程序运行之前,对程序源代码进行翻译。

解释型语言和编译型语言

解释型语言是在代码运行时,使用解释器对源代码进行解释,解释成机器码存储在内存中然后交由 CPU 运行。但是由于从解释到运行的过程非常短暂,而且是逐行解释,不能对代码进行全文分析,所以存在很多的重复工作。故其运行效率不高。

而编译型语言是在代码运行前,将代码编译为机器码,以可执行文件的方式存储在设备上。运行的效率非常高,避免了解释的动作。

换句话说,编译型语言使用编译器在程序运行之前就对代码进行编译,运行的时候 CPU 能直接执行机器码,故性能好,适合密集型计算的场景,对性能有较高要求的场景。而解释型语言其在运行之前需要对代码使用解释器进行解释,故运行效率低,适合做业务层级的代码编写,往往开发效率教编译型的语言更高。

但是,诸如 PHP、Python 这类的语言,虽然都是解释性语言,但也都运用了编译技术对代码提前进行编译,编译为字节码,然后运行时候对字节码翻译成机器码运行。以此来提升程序的运行效率。

综上所述,其实现在编译型语言和解释器语言的界限是比较模糊的。知其道理即刻,无需纠结。在高层的软件开发场景中,偏向于使用解释性语言,而底层的软件对性能要求较高,需要充分发挥机器性能,故适合使用编译型语言。

Python 解释器(Python Interpreter)

Python 的解释器有很多的版本,有使用 Java 编写的,有使用 C 语言编写的。一般我们使用的都是 C 语言编写的,我们称之为 CPython。下文中使用的也是这个版本。

每一个 Python 程序都会经理如下这段生命周期:

  1. 编写源代码

  2. 由 Python 的解释器将源代码编译成字节码

  3. Python 的虚拟机将执行字节码

第二步,由 Python 的解释器将源代码编译成字节码需要展开说明,相对比较复杂。

  1. 首先,Python 的解释器需要读取文件中的代码,整合分布在众多文件中的代码加载到内存中。

  2. 对这些代码进行语法的见擦汗,确保在语法层面是正确的。

  3. 然后,将 Python 的源代码编译成字节码。

  4. 最后,将这些字节码发送给 Python 的虚拟机(PVM, Python Virtual Machine)进行处理。

实际上, Python 的解释器还做了更多的工作:

  • 对于特定的操作系统平台的优化

  • 虚拟机的优化,比如操作码的预测

  • 字节码执行前的初始化

  • 其他

Python 字节码

Python 的字节码是一种不依赖于特定平台的中间语言,但是并不能完全保证同一份字节码在不同的平台的虚拟机上能够完全兼容,不同版本的 Python 也存在不兼容。

一般 Python 的字节码被存储为 .pyc 为后缀的文件中。相对于直接执行源代码,执行字节码要有效率得多。而且还可以对字节码进行缓存,如果代码没有改变的情况下,无需重新编译字节码。

我们编写一个最简单的 Python 程序, 命名为 test.py :

print('Hello World')

然后,我们可以模糊搜索以 test*.pyc 命令的字节码文件, 并看看其文件内容:

$ vim lib/python3.8/site-packages/setuptools/command/__pycache__/test.cpython-38.pyc

内容局部截图如下:

bytes code

在 Python 3.2 以及之后的版本中,Python 将字节码文件存储与 __pycache__的目录中,其命令为脚本文件名加上 Python 解释器的版本。

在 Python 3.8 中,新增了 PYTHONPYCACHEPREFIX 的设置,可以改变默认将字节码保存在 __pycache__目录中。

参考文档

  1. 《500 Lines or Less|A Python Interpretor written in python》

Last modified: 04 August 2024