目录

一、绪论

二、说明

2.1 字节码编译

2.2 Python 虚拟机 (PVM)

2.3 性能意义 ☆

2.4 开发意义

三、小结


一、绪论

Python 通常被描述为一种 解释型语言,在这类语言中,源代码在程序运行时被 “翻译” 成 “指令”,但这还不够确切。同许多解释型语言一样,Python 在正式处理代码前,内部先进行预处理,将 Python 源代码编译成字节码,然后将其转发至 Python 虚拟机中。换言之,Python 实际上将源代码编译为 “虚拟机” 的一组指令,而这种中间格式的指令便称为 “字节码”


二、说明

2.1 字节码编译

执行 Python 程序时,Python 内部将隐式地将 源代码 (source code) 编译成 字节码 (byte code)。编译可以理解为一种简单的 “翻译” 步骤,但有别于C、C++ 等静态语言的编译;字节码则是一种 低级的与平台无关的 代码表现形式,但有别于机器的二进制代码 (如 Intel 或 ARM 芯片的指令)。简言之,Python 将源代码语句的每一条分解为单一步骤,从而 “翻译” 为一组字节码指令。相比于文本文件中的 Python 源代码,字节码的运行速度更快。

注意,上述过程的发生对大多数用户而言是隐式的。若 Python 进程在机器上拥有写入权限, 那么它将把程序的字节码保存为一个 .pyc 文件 (即已编译的 Python 源文件 (.py compiled)) 。在 Python3 中,.pyc 字节码文件 被存储在名为 __pycache__ 的子目录中,该子目录位于与 .py 源文件相同的路径下。而 __pycache__ 子目录中的文件命名中包含了编译它们的 Python 的版本信息 (基于创建它的特定 Python 进制代码版本,如 test.cpython-36.pyc) 。例如:

在 Windows 下创建一个 test.py 文件:

然后在 IDE 中编译 (后续说明原因),在 test.py 所在目录下可以显式看到多出了一个 __pycache__ 子目录:

进入 __pycache__ 子目录中可以看到其字节码文件  test.cpython-36.pyc:

在记事本中打开,可见由于编码问题无法直接阅读,但可以看到 test.py 的程序主体也在其中:

事实上,当一个模块首次被导入或修改已编译的源文件时,都会在 .py 所在目录的 __pycache__ 的子目录下创建一个包含已编译代码的 .pyc 文件。新创建的 __pycache__ 子目录能够 避免太多文件挤在同一路径下,而新的字节码文件命名规范确保了同一主机上安装的不同版本的 Python 所生成的字节码文件 不会相互覆盖。当然,这些字节码文件都是自动生成的,与大多数 Python 程序无关,也随着不同版本的 Python 有着不同的形式。

上述 Python 保存字节码的方式是对 Python 程序 启动速度 的一种优化 (而非运行速度的优化)。下次运行程序时,若上次保存字节码后未再修改过源代码,并且使用同一个 Python 编译器版本运行, 那么 Python 将会加载 .pyc 文件并跳过编译步骤。该过程工作原理如下:

  • 源文件的改变:Python 会自动检查源文件和字节码文件最后一次修改的时间戳,以确认是否必须重新编译:若源代码被编辑并保存,则下次程序运行时,将自动重新创建字节码文件。
  • Python 的版本:导入 (import) 机制会检查是否需要因使用了不同的 Python 版本而重新编译,这些版本信息在 Python3 中存储于字节码文件名中间部分。

可见,源文件的修改和 Python 版本的改变都会触发新的字节码文件的编译。导入模块时,若同时存在 .py 和 .pyc 文件,Python 将优先使用 .pyc 文件运行;若 .pyc 文件的编译时间早于 .py 的时间,则将重新编译 .py 并更新 .pyc 文件。还有,字节码文件也是发布 Python 程序的方法之一。若 Python 只找到 .pyc 字节码文件,而未找到对应的原始 .py 源代码文件,它也很“乐意”运行该程序。

即便 Python 无法在机器上写入字节码, 程序也可以正常工作 —— 字节码会在内存中生成, 并在程序结束时直接被丢弃 (这就是在 IDE 中只点击“运行”不会看到 .pyc 文件的原因,若要显式查看还需要在 IDE 中手动点击“编译”)。然而,由于 .pyc 文件具有加速启动的作用,最好确保在大型程序中能够创建它们。

最后,字节码只会针对那些被导入 (import) 的文件而生成, 而不是顶层的执行脚本 (严格来说,这是一种针对“导入”的优化)。此外,文件仅在程序运行 (或编译) 时才会被导入,而在交互式命令行中输入的命令并不会生成字节码。


2.2 Python 虚拟机 (PVM)

一旦源程序编译成字节码 (或从已存在的 .pyc 文件中载入字节码),便会将字节码发送到被称为 Python 虚拟机 (Python Virtual Machine, PVM) 的程序上来执行。不同于大名鼎鼎的 JAVA 虚拟机 (JVM),Python 虚拟机 (PVM) 相对鲜为人知,其中一个原因在于 PVM 是更为知名的 Python 解释器 (Python Interpreter) 的一部分 (如 CPython 使用了基于堆栈的虚拟机)。但不同于诸如 Vmware 那种 系统虚拟机,此处指的是类似 JVM、CLR 的 程序虚拟机

常见的 Python 解释器有:

简单来说,Python 解释器 由一个 编译器 (compiler) 和一个 虚拟机 构成,编译器负责将源代码转换成字节码文件,而虚拟机负责执行字节码。所以,解释型语言其实也有隐式的编译过程,只不过该编译过程并非直接生成目标代码,而是生成中间代码 (字节码),然后再通过虚拟机来逐行解释执行字节码。

更具体地,PVM 并非一个独立程序,也无须安装。本质上,PVM 可以理解为一个 迭代运行字节码指令的 “大循环” —— 一个接一个地完成操作。PVM 作为 Python 的运行时引擎,时常表现为 Python 系统的一部分,并且作为实际运行脚本的组件。从程序运行的技术流程上看,PVM 是 Python 解释器运行程序的最后一步。下图展示了 Python 运行时的简化执行模型:

事实上,程序员编写的由源代码构成的 .py 源文件,先被自动编译为由字节码构成的 .pyc 字节码文件,然后被传递给 Python 虚拟机 (PVM) 中运行。注意,.pyc 文件是字节码在磁盘上的表现形式,而字节码在 PVM 程序里对应的是 PyCodeObject 对象 (import 模块时创建该对象)。PVM 会字节码当前的上下文环境中,从编译得到的 PyCodeObject 对象中逐条执行字节码指令,从而完成程序的执行。关于更深层的源码分析则大可不必深究。

总体上,操作系统中执行程序离不开两个概念:进程和线程。Python 通过 PyInterpreterState 和 PyTreadState 分别模拟进程和线程的概念。其中,每个 PyThreadState 都对应着一个帧栈,PVM 在多个线程上切换。当 PVM 开始执行时,它会先进行一些初始化操作,最后进入 PyEval_EvalFramEx 函数,它的作用是不断读取编译好的字节码并逐条执行,类似 CPU 执行指令的过程。PyEval_EvalFramEx 函数内部主要是一个 switch 结构,根据字节码的不同执行不同的代码。

总之,程序员只需简单地编写代码并运行文件,而 Python 会负责所有运行这些文件的逻辑。


2.3 性能意义

倘若熟悉 C 和 C++ 这类完全编译语言,则很容易发现 Python 运行模式中的一些差异。例如,Python 的执行流程中通常 没有 build / make 的步骤,而是写完代码立即执行 (run);Python 字节码并非机器的二进制代码 (如 Intel 或 ARM 芯片的指令)。其实,字节码是特定于 Python 的一种表现形式。这就是为什么 Python 代码难以运行得像 C 或 C++ 代码一样快,因为 PVM 循环 (而非 CPU 芯片) 仍需逐行解释字节码,并且相比于 CPU 指令,执行字节码指令需要更多的工作。另一方面,与其他经典的解释器不同,Python 仍有内部的编译步骤,即无需反复地重新分析、分解每行源代码语句的文本 (所以 Python 又比经典的解释型语言更快些)。在上述机制的综合作用下,Python 代码的运行速度介于传统的编译语言和传统的解释语言之间。


2.4 开发意义

Python 执行模型所导致的另一个结果是:Python 的开发和执行环境实际上并无区别。换言之,编译和执行源代码的系统是同一个系统。在 Python 中,编译器总是在运行时出现,并作为运行程序系统的一部分,从而将大大缩短开发周期,提升开发效率。在程序开始执行前,无需预编译和链接,只需简单地输入并运行代码即可。这同样让 Python 带上了更浓厚的 动态语言 色彩:在运行时, Python 程序去构建并执行另一个 Python 程序是可能的,且往往非常方便。例如,eval 和 exec 内置模块能够接受并运行包含Python 程序代码的字符串。上述结构是 Python 能够实现产品定制的原因:因为 Python 代码可以动态地被修改,用户可以改进系统内部的 Python 部分,而无需拥有或编译整个系统的代码。

总而言之,Python 完全不需要初始的编译阶段,所有的事情都是在程序运行时发生的,甚至包括建立函数和类的操作以及模块的链接。而这些工作对于静态语言而言,往往发生在执行之前。


三、小结


参考文献:

《Learning Python 5th》

你了解 Python 字节码的原理吗?_VIP_CQCRE的博客-CSDN博客

python程序是如何运行的? - 知乎

python虚拟机运行原理 - webber_liu - 博客园

python 解释器及其工作原理 - 小萍瓶盖儿 - 博客园

【Python】浅谈 字节码 + 虚拟机 (Python 解释器)相关推荐

  1. python.freelycode.com-Python字节码介绍

    Python部落(python.freelycode.com)组织翻译,禁止转载,欢迎转发. 了解Python字节码是什么,Python如何使用它来执行代码,以及了解它可以帮我们干什么. 如果你曾经编 ...

  2. 【python 笔记/小白快速入门python】python浅谈(一)犹抱琵琶半遮面

    python浅谈(一)犹抱琵琶半遮面 继浅谈(零)初识庐山真面目[https://blog.csdn.net/HarryOtter/article/details/90519877 ] 之后,终于窥得 ...

  3. python实例编程_浅谈如何编程Python3——Python实例(3)

    浅谈如何编程Python3--Python实例(3) # 测试实例一 print("测试实例一") str= "runoob.com"print(str.isa ...

  4. python源码剖析—— python中的字节码对象初探

    一.代码对象 每个初学python的人都会认为python是一种解释型语言,这个不能说错.但是python并不是真的对执行的python代码的每一行进行解释,虽然我们有一个所谓的"解释器&q ...

  5. Python - 浅谈Python的编译与反编译

    1 - Python编译过程涉及的文件 py 源代码文件,由python.exe解释,可在控制台下运行,可用文本编辑器进行编辑: pyc 源代码文件经过编译后生成的二进制文件,无法用文本编辑器进行编辑 ...

  6. 零基础python入门书籍-浅谈零基础自学python入门书

    作为一个计算机初级爱好者,还是比较喜欢Python,因为Python干净利索,简单直接. Python编写代码的速度非常的快,而且非常注重代码的可读性,非常适合多人参与的项目. 它具备了比以前传统的脚 ...

  7. 浅谈汇编器、编译器和解释器

    作者: 硬核老王 简单介绍一下编程方式的历史演变. -- Erik O'shaughnessy(作者) 在计算机诞生不久的早期年代,硬件非常昂贵,而程序员比较廉价.这些廉价程序员甚至都没有" ...

  8. 浅谈面试阿里外包python开发工程师

    本人也是抱着不成功则成仁的想法去的 先说一下肯定有干货 反正不管过不过 总的榨点他身上的东西 对吧! 抓到了总要一顿乱问 晚上7点半接到面试邀请电话 请先简单的自我介绍一下自己 吧啦吧啦说了有两三分钟 ...

  9. Python | 浅谈并发锁与死锁问题

    今天是 Python专题 的第24篇文章,我们一起来聊聊多线程场景当中不可或缺的另外一个部分-- 锁 . 很多人学习python,不知道从何学起. 很多人学习python,掌握了基本语法过后,不知道在 ...

最新文章

  1. 使用Heroku部署Rails应用
  2. linux学习笔记 (fork FILE PC)
  3. 上传图片即时显示图片
  4. 微信小程序 网络请求之设置合法域名
  5. 之前接触过的测试的相关工具
  6. 山东科技大学c语言完全题库,“山东科技大学十大未解之谜”之完全解答
  7. 利用python进行数据分析之准备工作(1)
  8. macOS和iOS间的隔空接力功能
  9. vm虚拟机的安装使用装系统有序列号
  10. 卫星地面站的星地链路研究
  11. 208个地级市和31个省市城乡泰尔指数(2010-2019年)
  12. wps中将二维表转换为一维表
  13. 《东周列国志》第九回 齐侯送文姜婚鲁 祝聃射周王中肩
  14. 使用git提交到仓库使用commit指令出现问题Your branch and 'origin/master' have diverged,
  15. 计算机专业对应的职业群有,[计算机硬件及网络]7k专业与职业群.ppt
  16. 数值分析12 - Hermite埃尔米特插值法(要求高阶插值误差足够小)
  17. pb3-protobuf 格式-上传 网络信息内容
  18. python对投标_有关招标投标签订合同的说法,正确的是(      ) 。
  19. 拆迁安置用房如何办理产权证
  20. js+css让背景图片动起来

热门文章

  1. with as的update写法
  2. 【Linux】冯诺依曼体系结构和操作系统概念
  3. 菜鸟实现 PHP 二阶、三阶行列式计算和矩阵运算的加、减、乘
  4. board oracle tarot_Thoth and the Tarot dpedtech(透特和塔罗牌dpedtech).pdf
  5. roads 用户体验标准_在你眼中的RoadMap,其实并没有那么难搞定
  6. supervisor的用法
  7. 2020年jQuery笔试题汇总整理
  8. 使用Groovy和7-Zip-JBinding解压缩7-Zip文件
  9. 爬虫【1】打开网站,获取信息
  10. 微信小程序实现登陆功能