Python|Python 解释器和三种栈

源代码就是程序员写的人类能够看懂的代码。我们编写的程序文件的执行需要两步:1、编译器将源码编译成二进制文件,文件里是字节码指令,字节码会存储在 PyCodeObject 对象中;2、虚拟机运行二进制文件,在运行时虚拟机会创建字节码执行的上下文环境,也就是下文提到的各种栈,使用 PyFrameObject 表示运行时的 “调用栈” 。Python 中这两步是合在一起的,由 Python 解释器完成。实际上 Python 解释器由编译器和虚拟机组成,将源文件编译为二进制文件这步由编译器完成,运行二进制文件这步由虚拟机来完成的,运行完成后可以看到二进制文件。Java 中这两步是分开的,先编译成二进制文件,再由 JVM(Java 虚拟机)执行。其实二者流程差不多,侧重不同。
Python 文件在运行之后会在同一个目录中出现 .pyc 文件,这是上面提到的缓存二进制文件,它有助于提高文件再次运行的效率,缓存文件通常保存在 __pycache__ 目录下,缓存文件中的数据就是只有机器能够识别的字节码,使用编辑器打开文件我们也看不懂。
Python 与大多数解释型语言一样,解释器在运行程序时首先将源代码编译为一组虚拟机指令,并且 Python 解释器是针对相应的虚拟机实现的。这种中间格式的虚拟机指令被称为 “字节码” ,也就是说 .pyc 这个二进制文件中存储的是字节码指令,这些指令只有对应的 Python 虚拟机可以运行。Python 被称为解释型语言,其中一个原因是如上所述的程序运行时源代码被转换成字节码指令。
最常用的 Python 解释器是 CPython ,我们说 "Python 解释器" 时默认就是指 CPython ,它使用三种类型的栈:
调用栈 call stack 、数据栈 data stack 、块栈 block stack
【Python|Python 解释器和三种栈】调用栈 call stack 是运行 Python 程序的主要结构。它为每个当前活动的函数调用创建一个东西 —— “帧 frame”,也叫栈帧,调用栈的栈底是程序的入口点。每个函数调用推送一个新的帧到调用栈,每当函数调用返回后,这个帧被销毁。
在每个帧中,有一个数据栈 data stack(也称为计算栈 evaluation stack)。数据栈就是 Python 函数运行的地方,运行的 Python 代码大多数是由推入到这个栈中的内容组成的,解释器操作它们,然后在函数返回后销毁它们。
在每个帧中,还有一个块栈 block stack 。它被 Python 用于跟踪某些类型的控制结构:循环、try / except 块以及 with 块,将语句块全部推入到块栈中,当退出这些控制结构时,块栈被销毁。这将帮助 Python 了解任意给定时刻哪个块是活动的,例如 continue 或者 break 语句可能影响块的运行。
In [34]: import disIn [35]: def hello(): ...:print('Hello World') ...:In [36]: dis.dis(hello) 20 LOAD_GLOBAL0 (print) 2 LOAD_CONST1 ('Hello World') 4 CALL_FUNCTION1 6 POP_TOP 8 LOAD_CONST0 (None) 10 RETURN_VALUE

    推荐阅读