当前位置: 首页 > java >正文

2.1 Python解释器工作原理

一、目的

前面的几节我们对环境和库进行了介绍,现在开始正式进入Python代码的学习。首先,我们需要理解Python解释器的工作原理,以更好地体会代码的执行过程。我们已经知道,Python解释器会逐条执行输入的代码,但尚不清楚具体执行机制。了解完整执行流程有助于深入理解Python解释器,而非将其视为黑盒,这对编写代码具有重要意义。

二、工作原理简介

解释器可分为前端和后端两部分:前端包括词法分析器、语法分析器和编译器,后端包含Python虚拟机(PVM)和标准库。前端将Python代码翻译为PVM可执行的字节码,后端的PVM是解释器的核心执行部件,与传统虚拟机不同,它不模拟完整硬件结构,而是作为字节码执行器逐条处理指令。至于为什么不直接让PVM执行Python代码,这是为了提高解释器的灵活性、性能和可维护性。字节码比源码更接近机器指令,虚拟机的执行效率更高。将源码转换为字节码是一个固定且耗时的过程,但只需执行一次,转换为字节码后,后续只需运行字节码,可以避免重复解析。字节码作为平台无关的中间表示,使同一代码能在不同系统的PVM上运行,仅需适配操作系统即可实现跨平台特性。

字节码主要由可执行指令和常量表构成,通常每行代码对应一条或多条指令,代码中的不可变数据存入常量表。Python数据分为运行时可变与不可变类型,为保障安全性和提升性能,编译阶段就将不可变数据嵌入字节码,PVM执行时这些数据已随指令载入内存。可变数据则需运行时动态创建并分配内存,这正是动态语言灵活性的体现。

Python中的数据分为整数、浮点数、字符串、列表、元组、字典等类型,这些统称为对象。代码中对象可通过变量名(字符串符号)标记,即赋值给变量。PVM运行时,变量本质是内存地址(64位系统下为64位数,表示对象起始地址)。传统教程将数据类比为装数字的盒子,这对C语言等编译型语言适用,但在Python中易产生误解。更恰当的比喻是将Python数据视为我们的房产、车辆、工厂等资产,变量则是使用这些资产的钥匙。这些资产通常规模不小,如同Python对象至少占用数十字节。对象内存除存储实际数据外,还包含引用计数、类型指针、预分配空间、跟踪信息等额外开销,这些设计源于Python的动态特性与对象模型。正如房屋不仅放置床铺,车辆也不只具备车轮。此外,我们的资产分为依附于土地的不动产和可随意移动的可动产,Python中的对象也分为不变对象和可变对象。

三、实验

如果读者好奇字节码长什么样,或者什么是词法分析、语法分析、编译,可以进行我们接下来的操作步骤。首先创建一个新虚拟环境,打开Anaconda Prompt,执行下面的命令。

conda create -n compile python=3.9 -y
conda activate compile
pip install graphviz

之后创建一个名为target.py的文件,在文件中写入想要编译的代码,本文的代码如下,这是一个找列表中最大值的程序。

def find_max(numbers):if not numbers:return Nonemax_num = numbers[0]for num in numbers[1:]:if num > max_num:max_num = numreturn max_numif __name__ == '__main__':test_numbers = [3, 1, 4, 1, 5, 9, 2, 6]print(f"数组 {test_numbers} 中的最大值是: {find_max(test_numbers)}")empty_list = []print(f"空数组的最大值是: {find_max(empty_list)}")

最后,我们将下面的代码保存到和target.py同目录的文件中并执行,这是用于生成中间结果的脚本,包括词法分析、语法分析和编译的结果,分析结果会保存到同目录下的txt文件中,另外,语法分析生成的抽象语法树会保存到一个pdf文件中。

import dis
import ast
import inspect
import tokenize
import target
from io import BytesIO
from graphviz import Digraphdef plot_ast(node, graph=None, parent=None, edge_label=""):if graph is None:graph = Digraph()node_name = str(id(node))label = type(node).__name__if hasattr(node, 'ctx'):label = f"{type(node).__name__}(id='{getattr(node, 'id', '')}')" if hasattr(node, 'id') else type(node).__name__graph.node(node_name, label=label)if parent:graph.edge(parent, node_name, label=edge_label)for field, value in ast.iter_fields(node):if field == 'ctx':continueif isinstance(value, ast.AST):plot_ast(value, graph, node_name, field)elif isinstance(value, list):for item in value:if isinstance(item, ast.AST):plot_ast(item, graph, node_name, field)return graphdef analysis():save_path = 'analysis_result.txt'file = open(save_path, 'w', encoding='utf-8')code = inspect.getsource(target)code_bytes = code.encode('utf-8')tokens = tokenize.tokenize(BytesIO(code_bytes).readline)print('词法分析结果:', file=file)for token in tokens:print(f"\tType: {tokenize.tok_name[token.type]}, "f"Value: {token.string!r}, "f"Position: {token.start}", file=file)print("\n\n语法分析结果:", file=file)tree = ast.parse(code)print(ast.dump(tree, indent=2), file=file)graph = plot_ast(tree)graph.render("ast_tree", view=True)code_obj = compile(tree, filename="<ast>", mode="exec")print("\n\n反汇编后的字节码:", file=file)dis.dis(code_obj, file=file)print("实际字节码(十六进制):", code_obj.co_code.hex(), file=file)print("常量表:", code_obj.co_consts, file=file)if __name__ == '__main__':analysis()

http://www.xdnf.cn/news/14351.html

相关文章:

  • [深度学习]目标检测基础
  • leetcode 1432. 改变一个整数能得到的最大差值 中等
  • MQTT:构建高效物联网通信的轻量级协议
  • Python实战项目 贪吃蛇 源码分享 毕业设计
  • 自动驾驶系统研发系列—激光雷达干扰实战:自动驾驶安全的隐形陷阱
  • (LeetCode 动态规划(基础版)) 518. 零钱兑换 II (动态规划dp)
  • Python训练营打卡 Day54
  • ONLYOFFICE 协作空间 企业版使用秘籍-5.企业电子文件如何管理?便于查找、访问和协作,轻松提升效率
  • 操作系统八股文
  • Python密码加密与校验详解
  • python profiling
  • (十六)GRU 与 LSTM 的门控奥秘:长期依赖捕捉中的遗忘 - 更新机制对比
  • ShardingSphere 全面学习路径
  • 编译链接实战(31)再论静态库的本质是啥
  • LeetCode 2300.咒语和药水的成功对数
  • leetcode复盘(1)
  • 【项目实训】【项目博客#08】HarmonySmartCodingSystem系统前后端知识图谱与可视化实现(5.12-6.1)
  • 深入理解Redis五种基本数据类型
  • (LeetCode 动态规划(基础版)) 279. 完全平方数 (动态规划dp)
  • java复习 14
  • 深度学习入门知识
  • DP刷题练习(二)
  • 【学习笔记】深入理解Java虚拟机学习笔记——第7章 虚拟机类加载机制
  • 关于layui select option莫名多一行的问题
  • EventSourcing.NetCore:基于事件溯源模式的 .NET Core 库
  • 内部代理服务器单点故障导致后端服务概率性失败
  • 算法的几大模块
  • SpringBoot入门 springboot模版注入问题
  • 单例模式的好处
  • 火山引擎解码生态型增长铁律