『Python底层原理』--CPython如何运行Python代码

Python作为一种广泛使用的编程语言,其简洁的语法和强大的功能深受开发者喜爱。

然而,对于许多Python用户来说,CPythonPython的官方实现)的内部工作机制仍然是一个神秘的黑盒。

今天,我们将继续探索CPython的源代码,尝试了解Python从程序启动到字运行的神秘面纱。

1. CPython代码概要

CPython的代码库规模庞大,包含约35万行C代码 和近60万行Python代码

这些代码分布在多个目录中,这些目录结构为CPython的开发和维护提供了清晰的组织方式。

主要的目录包括:

  • Grammar/:Python语法文件
  • Include/:头文件
  • Lib/:标准库模块(Python实现)
  • Modules/:标准库模块(C实现)
  • Objects/:内置类型实现。
  • Parser/:解析器相关代码
  • Programs/:可执行文件的源码
  • Python/:解释器核心代码

CPython是开源的,代码托管在gihtub上,感兴趣的话,可以下载了查看。

下载了之后,可以切换到自己感兴趣的分支版本。

bash 复制代码
git clone https://github.com/python/cpython/ && cd cpython
git checkout 3.12

编译源码过程相对简单,只需运行以下命令即可:

bash 复制代码
./configure
make
make test
sudo make install

编译完成后,运行 ./python.exe 即可启动自己编译的CPython版本。

2. 启动Python

CPython的入口点是 main() 函数,位于 Programs/python.c 文件中。

c 复制代码
/* Minimal main program -- everything is loaded from the library */

#include "Python.h"

#ifdef MS_WINDOWS
int
wmain(int argc, wchar_t **argv)
{
    return Py_Main(argc, argv);
}
#else
int
main(int argc, char **argv)
{
    return Py_BytesMain(argc, argv);
}
#endif

这个函数是程序启动的起点,负责初始化CPython并开始执行用户代码。

Windows平台上,CPython使用 wmain() 作为入口点,以支持UTF-16编码的命令行参数。这种设计使得CPython能够更好地处理不同平台上的字符编码问题。

main()函数的主要职责是调用Py_Main()Py_BytesMain(),这两个函数分别处理宽字符和字节字符串的命令行参数。

Py_MainPy_BytesMain位于 Modules/main.c 文件中。

这两个函数进一步调用pymain_main(),开始CPython的初始化过程。

Py_MainPy_BytesMain以及pymain_main这些函数都在Modules/main.c 文件中。

3. 初始化Python

CPython的初始化过程分为三个阶段预初始化核心初始化主初始化,每个阶段都有其特定的任务和目标。

  1. 预初始化:

预初始化阶段主要负责设置运行时状态、默认内存分配器和基本配置。

这一阶段通过调用 _PyRuntime_Initialize()PyPreConfig_InitPythonConfig() 等函数来完成。

这些函数初始化了CPython的全局运行时状态,并为后续的初始化阶段做好了准备。

其中,_PyRuntime_Initialize函数的实现位于:Python/pylifecycle.c

PyPreConfig_InitPythonConfig函数的实现位于:Python/preconfig.c

  1. 核心初始化:

核心初始化阶段是CPython初始化的关键部分。

这一阶段初始化了主解释器状态、线程状态、内置类型、 builtins 模块、 sys 模块和导入系统。

这些组件构成了Python运行的核心基础,使得CPython能够开始执行Python代码。

核心初始化通过调用 Py_InitializeFromConfig() 函数来完成,该函数进一步调用了 pyinit_core() 等函数,逐步构建了Python运行时的核心环境。

其中,Py_InitializeFromConfig函数的实现位于:Python/pylifecycle.c

  1. 主初始化:

主初始化阶段是CPython初始化的最后一步。

这一阶段完成了CPython的全面初始化,包括设置 sys.path 、导入 site 模块等。

这些任务使得CPython能够支持完整的Python功能,包括模块导入和脚本执行。

主初始化通过调用 pyinit_main() 函数来完成,该函数进一步调用了 init_interp_main() 等函数,完成了CPython的最终配置。

其中,pyinit_maininit_interp_main函数的实现位于:Python/pylifecycle.c

4. 运行Python

初始化完成后,CPython进入程序运行阶段。Py_RunMain() 函数(Modules/main.c文件中)是这一阶段的核心,它负责运行Python程序并进行清理。

根据不同的运行模式(如脚本、模块、命令行等), Py_RunMain() 函数调用不同的函数来执行代码。

例如, pymain_run_python() 函数处理 sys.path 的设置和模块的导入,确保Python程序能够在正确的环境中运行。

5. 编译和执行

Python代码的编译和执行是CPython运行的核心部分。

PyRun_FileExFlags()函数(Python/pythonrun.c文件中)是这一过程的入口点,它负责将Python代码编译为字节码,并将其加载到运行时环境中。

编译过程通过调用 _PyAST_Compile() 函数(Python/compile.c文件中)完成,该函数将抽象语法树(AST)编译为代码对象。

最终,PyEval_EvalCode() 函数(Python/pythonrun.c文件中)执行代码对象,进入字节码执行循环

字节码执行循环CPython运行的最后阶段。

这一阶段通过调用 _PyEval_EvalFrame() 函数(Include/internal/pycore_ceval.h文件中)来执行字节码指令,_PyEval_EvalFrame() 函数是一个复杂的函数,它负责处理各种Python操作,包括函数调用、变量访问和异常处理等。

如果想更好地理解Python的运行机制,可以深入研究这个函数。

6. 总结

本文主要从CPython源代码的角度来了解Python程序从启动到执行的全过程。

源代码是基于CPyhton 3.12 分支的。

通过阅读和分析CPython的源代码,我们不仅能够更好地理解Python的内部机制,还能够发现优化和改进的机会。

如果对CPython的内部工作机制感兴趣,最好能够亲自探索其源代码。通过阅读和理解源代码,将获得更深入的知识,并能够更好地利用Python的强大功能。

相关推荐
SamChan908 分钟前
用Python+Requests批量翻译PDF:从脚本到调度
后端·python·microsoft·ai·pdf·机器翻译
草莓熊Lotso9 分钟前
【LangChain】核心技术:嵌入模型与向量存储完全指南
服务器·网络·python·langchain
leisoo80978 小时前
100GBA股股票数据怎么存ClickHouseRedisMySQLJSON完整对比
大数据·linux·服务器·开发语言·python
WangYan20228 小时前
基于XGBoost与AI的生态—地学多源数据建模:植被与土地利用识别、土壤碳氮空间预测、生物多样性驱动机制、土壤微生物功能预测、生态退化与风险识别
python·机器学习·xgboost
金銀銅鐵10 小时前
[Python] 借助 turtle 逐字展示唐诗《金缕衣》
python
Python大数据分析@12 小时前
使用大模型MCP采集数据,爬虫已经无门槛
python·网络爬虫
quantdash_cc13 小时前
告别自建 Requests/BS4 网页爬虫:基于 QuantDash 搭建零维保的高性能量化行情流水线
开发语言·爬虫·python·pandas·量化·quantdash
65岁退休Coder13 小时前
LangChain v1.3.4 笔记 - 07 补充:链式调用 LCEL
后端·python·langchain
卷无止境13 小时前
FastAPI 部署在 Nginx 后面到底该怎么配
后端·python
半亩码田13 小时前
C#转Python第3.1篇:Python 的 class 没有访问修饰符?面向对象的另一条路
开发语言·python·c#