首页  >  文章  >  后端开发  >  Python 的内部工作原理

Python 的内部工作原理

Susan Sarandon
Susan Sarandon原创
2024-11-04 13:45:02420浏览

Internal Working Of Python

这是完整的代码文件:代码

1.源代码

当您编写 Python 脚本时,它是人类可读的文本。这个源代码是一切的起点。

您的 Python 源代码以 .py 文件编写,是人类可读的。此代码定义了程序的功能,指定变量、函数、循环等。

2.编译为字节码(编译器)

当你运行Python程序时,第一步是将源代码编译为字节码。这是由 Python 解释器完成的:

  • 语法检查:确保没有语法错误。
  • 编译:将高级源代码转换为字节码,这是一种较低级别的、与平台无关的表示形式。该字节码通常驻留在 __pycache__ 目录中的 .pyc 文件中。
    • 编译器:Python 使用解释器,但它首先将源代码编译为称为字节码的较低级别形式。
  • 标记化:将代码分解为称为标记的小块(如关键字、运算符、标识符)。
  • 解析:分析标记以确保它们遵循 Python 的语法规则。
  • 控制流图(CFG):表示程序在执行过程中可能遍历的所有路径。
  • 字节码生成:将解析后的令牌转换为字节码,这是Python虚拟机(PVM)的一组指令。

Internal Working Of Python

让我们深入探讨一下。

Python 编译器:尽管 Python 被称为解释性语言,但它确实有一个编译步骤。详细内容如下:

标记化

  • 将代码分解为称为标记的小块(如关键字、运算符、标识符)。
  1. 源代码:从您编写的代码开始。
  2. 分词器 (Lexer):这会将源代码分解为称为标记的较小部分,例如关键字(for、if)、运算符(、-)、标识符(变量名称)和文字(例如数字或字符串) .
  3. 解析:分析标记以确保它们遵循 Python 的语法规则。
  4. 语法分析:解析器获取这些标记并根据 Python 的语法规则检查它们。
  5. 解析树:从标记构建树结构,表示代码的语法结构。
  6. 语义分析:确保代码在数据类型、范围和其他特定于上下文的规则方面有意义。
  7. 控制流图(CFG):表示程序在执行过程中可能遍历的所有路径。
    • 控制流图:表示代码执行过程中可能采取的所有可能路径。
    • 节点和边:每个节点代表一个基本代码块,边代表从一个块到另一个块的控制流。
  8. 字节码生成:将解析后的令牌转换为字节码,这是Python虚拟机(PVM)的一组指令。
    • 字节码是源代码的更紧凑、较低级别的表示形式,并针对执行进行了优化。它独立于平台,这意味着它可以在任何具有兼容 PVM 的系统上运行。
    • 字节码:解析后的代码被转换为字节码,一种较低级别的、与平台无关的表示。
    • 指令集:该字节码是Python虚拟机(PVM)可以执行的一组指令。字节码存储在 __pycache__ 目录中的 .pyc 文件中,以加快将来的执行速度。

3.加载字节码(字节码)

编译后,Python 虚拟机加载字节码:

  • 从缓存中读取:如果字节码之前已编译且未更改,则从缓存(__pycache__)中读取。这通过跳过编译步骤来加快执行速度。
    • 字节码已加载到内存中,准备执行。然后字节码由 PVM 执行,解释指令以执行程序的任务。

4.由 PVM (PVM) 执行

PVM 现在解释并执行字节码:

  • 指令执行:PVM 读取每个字节码指令并执行它。每条指令对应一个特定的操作,例如加载值、执行算术或调用函数。
  • 内存管理:管理变量和对象的内存分配和释放。

Python 中的内存管理:

  1. 引用计数:Python 跟踪内存中对象的引用数量。当引用计数降至零时,可以回收该对象占用的内存。
  2. 对象分配:代码运行时在内存中创建 Python 对象(如整数、字符串、列表)。
  3. 垃圾收集:Python 有一个垃圾收集器,它通过释放不再使用的内存(即引用计数为零的对象)来帮助管理内存。
  4. 内存池:Python 使用内存池来更有效地分配小对象。这种池化有助于减少频繁分配和释放小块内存的开销。
  5. 内存优化:Python 应用各种优化来最小化内存使用,例如:
    • PVM 执行各种运行时优化以提高效率,例如某些实现(如 PyPy)中的即时 (JIT) 编译。
    • 重用小整数和内部字符串。
    • 有效管理数据结构(例如元组、列表、字典)。

示例

  • 字节码缓存:PVM 缓存已编译的字节码,以避免每次都重新编译源代码。这会加快后续运行的速度。
  • 常量折叠:这涉及在编译时而不是运行时简化常量表达式。例如,3 * 2 可能会预先计算为 6。

所以,总而言之:PVM 就像一个管弦乐队指挥,无缝地将字节码转换为计算机可以执行的操作。它的美妙之处在于,得益于 PVM,Python 代码是可移植的,无需修改即可在不同平台上运行。

我们如何查看字节码是否生成?

导入Python模块时,Python会将源代码编译为字节码并将其存储在__pycache__目录中。这有助于加快未来的导入速度,避免每次导入时都需要重新编译模块。

流程如下:

  • 首次导入:首次导入模块时,Python 会将 .py 文件编译为字节码。
  • pycache 目录:字节码存储在 __pycache__ 目录中,名称类似于 module_name.cpython-312.pyc。 # 312 是 Python 版本。
  • 后续导入:在后续导入时,Python 会检查 __pycache__ 目录中是否有已编译的字节码,如果源代码未更改,则使用它,从而加快导入过程。

例子:

我们有 byte.py。当我们在执行 byte.py 后从 hello_world.py 导入代码时,我们可以看到该特定文件夹中会有一个目录 __pycache__ ,并且我们可以看到 .pyc 文件:

from hello_world import greet

greet("Byte code")


通过使用 py_compile

py_compile模块,它允许你将Python源文件编译成字节码文件。这是一种加快未来运行脚本执行速度的便捷方法。

在 byte.py 中

import py_compile

py_compile.compile('hello_world.py')

  • py_compile 模块将 hello_world.py 编译为字节码。
  • 生成的字节码存储在 pycache 目录中,创建一个名为 hello_world.cpython-38.pyc (或类似文件,具体取决于您的 Python 版本)的文件。

生成字节码:

  • 执行整个脚本来生成字节码。这意味着任何顶级代码(例如 print("Hello, World!") 和 print("c"))都将在编译过程中运行。

生成的字节码:

  • 字节码包含所有函数、类和可执行语句,Python 使用它们来加速未来脚本的导入。

显示模块

Python 中的 dis 模块用于将字节码反汇编成更易读的形式。这可以帮助您了解 Python 代码在幕后的用途。它对于调试或了解 Python 的内部结构特别有用。

  • 在internal.py中我们有
from hello_world import greet

greet("Byte code")


输出

import py_compile

py_compile.compile('hello_world.py')

  • 程序首先导入 dis 模块,这是一个用于分析 CPython 字节码的强大工具。 CPython 是 Python 的默认实现,字节码是 Python 解释器的中间语言。
  • 接下来,我定义了一个名为greet的简单函数。该函数接受参数名称并打印出问候语。尽管函数本身非常简单,但 Python 底层发生的事情比表面上看起来更复杂。
  • disassemble_function 函数使用 dis.dis() 来反汇编greet函数。 dis.dis() 将 Python 函数转换为 Python 虚拟机实际执行的低级字节码。这个字节码是Python对greet函数的解释,更接近机器代码。
  • 当脚本调用 disassemble_function() 时,控制台输出显示我们的greet 函数的字节码。

这是字节码告诉我们的内容:

  • LOAD_GLOBAL(0):此操作码用于加载全局变量,在本例中是打印函数。
  • LOAD_CONST(1):这会将常量值“Hello,”加载到堆栈上。
  • LOAD_FAST(0):此操作码将局部变量名称加载到堆栈上。
  • FORMAT_VALUE(0):这会格式化我们的名称字符串,准备将其插入到即将构建的字符串中。
  • BUILD_STRING(2):这采用堆栈上的前两个值(“Hello,”和名称)并构建最终字符串。
  • CALL_FUNCTION(1):这一行调用函数(我们加载到堆栈上的全局打印函数),参数计数位于括号中(我们有一个参数,即我们的格式化字符串)。
  • POP_TOP:这会删除堆栈顶部(上一次调用的结果,因为 print 返回 None)。
  • LOAD_CONST(0):不加载。
  • RETURN_VALUE:这是greet函数的返回值,由于没有明确的return语句,因此为None。
  • 本质上,字节码显示了Python执行我们的greet函数时执行的各个操作。理解这些指令对于开发人员理解 Python 如何执行代码、优化函数和管理资源至关重要 - 当我们运行 Python 代码时,所有这些都在幕后无缝地发生。

这不是一次令人愉快地深入 Python 机房的经历吗?继续编码并继续探索该语言引擎室的深度?!

以上是Python 的内部工作原理的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn