python代码执行过程
在python程序运行时,python首先会编译生成“字节码”,之后将字节码发送到所谓的“虚拟机”上执行。
字节码是一种低级的,与平台无关的表现形式。字节码可以提高执行速度,比起最初的源代码文件,字节码的运行效率高得多。
在Python3.2之后,python在执行代码的时候会生成一个名为__pycache__的子目录,在该目录中存放一个名为“文件名+解释器+python版本+.pyc”的文件,这个文件就是字节码。
在下一次运行python程序的时候,如果没有对源代码进行更改或者是python版本进行更改,那么python将会直接加载.pyc文件。
源文件的改变:
python会自动检查源文件和字节码文件最后一次修改的时间戳,来确认是否需要重新编译。
python解释器版本的改变:
.pyc文件的名称中含有python的版本,导入机制会检查名称中的python版本,来确认是否需要重新编译。
最后,请记住字节码只针对哪些被导入(import)的文件而生成,而不是顶层的执行脚本。例如,我们有两个python代码,如下所示:
# my.py文件内容
from t import fun # 从模块t导入fun函数
fun()
print("asda")
# t.py文件内容
def fun():
print("Hello World!")
现在执行my.py文件,就可以看到自动创建__pycache__目录,并且生成的.pyc文件名字为:
t.cpython-38.pyc
文件名中的t表明字节码确实只针对哪些被导入(import)的文件而生成,而不是顶层的执行脚本。cpython是我使用的python解释器,38表示我所使用的python版本是python3.8。
python虚拟机
字节码会被发送到python虚拟机(python virtual machine)上执行。PVM不是一个独立的程序,它是python的运行时引擎,它是python解释器的最后一步。
python代码的执行过程应该是:
源代码-->字节码-->PVM
值得一提的是,python解释器和其他传统解释器不同,它包含了内部编译步骤。python并不需要反复重新解析源代码。这决定了python代码的执行速度还是可以的。
在python中,我们只拥有运行时,因为编译器总是在运行时出现,并且是运行程序系统的一部分。因为python不需要预处理,编译,汇编,链接这些处理步骤。这就可以让python做到非常好的动态语言的体验。
所有的事情都是在程序运行时发生的,包括建立函数,类以及模块的链接。
Python的各种实现
CPython
CPython是Python标准的实现。除了CPython之外,还有其他几种主要的Python实现。分别是:Jython,IronPython,PyPy等,其中Jython,IronPython能够提供直接和Java和.NET组件的使用接口。PyPy是一个现成的CPython替代品,它能更快的运行大多数的程序。
CPython是最初的,标准的Python实现。它由ANSI C编写而成。你从python官网(https://www.python.org/)获取的Python就是CPython。绝大多数的Linux系统附带的Python也是CPython。
Jython
Jython是基于Java实现的Python,其目的是与Java集成。Jython将Python源代码编译成Java字节码,并将字节码发送到Java虚拟机(JVM)上执行。所以python代码在运行时就像真正的Java程序一样。
Jython的目标是让python能够脚本化Java程序,以及Java程序可以将Python当作内嵌语言来运行。
IronPython
同样,IronPython的目的和Jython类似,是为了让python和.NET应用程序能够集成。IronPython项目起初由微软开发,后来将其开源。
PyPy
PyPy是CPython标准的另一个实现,它更加注重性能。它提供了一个带有即时编译器(JIT)的实现。即时编译器是指在运行代码的时候进行动态编译,而不是运行前编译好。JIT实际上是PVM的一个扩展,它将字节码中的部分直接编译为机器指令。因此,PyPy的性能将更好(几乎能跟C代码一样快),同时占用的内存可能更少。
PyPy回避了垃圾回收(GC)机制,这导致它的非引用计数方案意味着临时文件不会立即关闭,甚至有时候需要手动释放。
最后
上述的Python运行时结构,目前看来是不太可能会有大的改变。因此,字节码依旧会是一种标准。Python不太可能真的去实现静态编译,这将会破坏python本身的设计。