『Python底层原理』--CPython如何运行Python代码
Python
作为一种广泛使用的编程语言,其简洁的语法和强大的功能深受开发者喜爱。
然而,对于许多Python
用户来说,CPython
(Python
的官方实现)的内部工作机制仍然是一个神秘的黑盒。
今天,我们将继续探索CPython
的源代码,尝试了解Python
从程序启动到字运行的神秘面纱。
1. CPython代码概要
CPython
的代码库规模庞大,包含约35万行C代码和近60万行Python代码。
这些代码分布在多个目录中,这些目录结构为CPython
的开发和维护提供了清晰的组织方式。
主要的目录包括:
Grammar/
:Python语法文件Include/
:头文件Lib/
:标准库模块(Python实现)Modules/
:标准库模块(C实现)Objects/
:内置类型实现。Parser/
:解析器相关代码Programs/
:可执行文件的源码Python/
:解释器核心代码
CPython
是开源的,代码托管在gihtub
上,感兴趣的话,可以下载了查看。
下载了之后,可以切换到自己感兴趣的分支版本。
git clone https://github.com/python/cpython/ && cd cpython
git checkout 3.12
编译源码过程相对简单,只需运行以下命令即可:
./configure
make
make test
sudo make install
编译完成后,运行 ./python.exe
即可启动自己编译的CPython
版本。
2. 启动Python
CPython
的入口点是 main()
函数,位于 Programs/python.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_Main
和Py_BytesMain
位于 Modules/main.c
文件中。
这两个函数进一步调用pymain_main()
,开始CPython
的初始化过程。
Py_Main
,Py_BytesMain
以及pymain_main
这些函数都在Modules/main.c
文件中。
3. 初始化Python
CPython
的初始化过程分为三个阶段:预初始化、核心初始化和主初始化,每个阶段都有其特定的任务和目标。
- 预初始化:
预初始化阶段主要负责设置运行时状态、默认内存分配器和基本配置。
这一阶段通过调用 _PyRuntime_Initialize()
和 PyPreConfig_InitPythonConfig()
等函数来完成。
这些函数初始化了CPython
的全局运行时状态,并为后续的初始化阶段做好了准备。
其中,_PyRuntime_Initialize
函数的实现位于:Python/pylifecycle.c
PyPreConfig_InitPythonConfig
函数的实现位于:Python/preconfig.c
- 核心初始化:
核心初始化阶段是CPython
初始化的关键部分。
这一阶段初始化了主解释器状态、线程状态、内置类型、 builtins 模块、 sys 模块和导入系统。
这些组件构成了Python
运行的核心基础,使得CPython
能够开始执行Python
代码。
核心初始化通过调用 Py_InitializeFromConfig()
函数来完成,该函数进一步调用了 pyinit_core()
等函数,逐步构建了Python
运行时的核心环境。
其中,Py_InitializeFromConfig
函数的实现位于:Python/pylifecycle.c
- 主初始化:
主初始化阶段是CPython
初始化的最后一步。
这一阶段完成了CPython
的全面初始化,包括设置 sys.path 、导入 site 模块等。
这些任务使得CPython
能够支持完整的Python
功能,包括模块导入和脚本执行。
主初始化通过调用 pyinit_main()
函数来完成,该函数进一步调用了 init_interp_main()
等函数,完成了CPython
的最终配置。
其中,pyinit_main
和init_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的强大功能。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 本地部署 DeepSeek:小白也能轻松搞定!
· 传国玉玺易主,ai.com竟然跳转到国产AI
· 自己如何在本地电脑从零搭建DeepSeek!手把手教学,快来看看! (建议收藏)
· 我们是如何解决abp身上的几个痛点
· 如何基于DeepSeek开展AI项目