Python解释器是用什么语言写的?CPython底层实现解析
日常在终端中输入 python 或 python3 启动的官方默认解释器,全称是 CPython,它是用 C 语言(早期基于 ANSI C/C89,现代版本基于 C11 标准) 和 Python 语言自身 共同编写的。其中核心运行时、虚拟机与对象系统由 C 语言构建,高层标准库与辅助工具模块则采用 Python 编写。
一、语言规范与解释器实现的分离
理解 Python 是用什么语言写的,首先需要明确一个核心概念:Python 是一门编程语言的语法规范,而解释器是执行这套规范的具体软件程序。 只要遵循 Python 官方制定的语言规范(PEP 标准与语法树语义),任何通用系统级编程语言都可以用来编写 Python 解释器。
| 解释器名称 | 主要实现语言 | 运行平台与核心特点 | 典型适用场景 |
|---|---|---|---|
| CPython | C 语言(C11/C99)+ Python | 跨平台(C 运行时),官方参考实现,生态支持最完备 | 生产通用环境、数据科学、机器学习全领域 |
| PyPy | RPython(Restricted Python) | 内建 JIT(即时编译器),纯 Python 数值与循环性能极高 | 纯 Python 长生命周期后台服务、高密集循环计算 |
| Jython | Java | 运行在 JVM(Java 虚拟机)之上,可与 Java 类库双向调用 | 传统企业级 Java 系统扩展脚本(目前主要停留在 2.7) |
| IronPython | C# | 运行在 .NET / CLR 平台,无缝使用 .NET Framework 类库 | Windows 桌面应用插件、.NET 生态混合开发 |
| MicroPython | C 语言(专为嵌入式优化) | 紧凑高效,占用几十 KB 内存即可在单片机上运行 | 微控制器、物联网(IoT)嵌入式开发 |
| RustPython | Rust | 基于 Rust 语言开发,主打内存安全性与无 GIL 探索 | WebAssembly 浏览器端嵌入、Rust 项目内嵌脚本 |
在所有实现中,CPython 拥有超过 95% 以上的占有率。Guido van Rossum 创造 Python 时首选 C 语言,是因为 C 语言具备极高执行效率、能够直达操作系统底层内核调用,并且几乎所有主流操作系统都默认提供高效的 C 编译工具链与外部库互操作接口。
二、CPython 源代码仓库目录划分
在 CPython 的官方开源代码仓库中,整体设计清晰地体现了 C 与 Python 的分工。以 Python 3.12 核心代码库为例,根目录下最关键的几个子目录分工如下:
Include/:存放全部 C 语言头文件(.h)。定义了诸如PyObject、PyTypeObject等全局核心结构体与暴露给外部调用的 Python/C API。Objects/:存放所有内置数据类型的 C 语言实现,例如整型(longobject.c)、列表(listobject.c)、字典(dictobject.c)和类元信息。Python/:解释器的核心逻辑引擎。包括字节码执行主循环(ceval.c)、编译器前端(compile.c)、抽象语法树解析(ast.c)以及初始化流程。Parser/:词法分析器与语法分析器(如现代 PEG 解析器pegen.c),负责将 Python 脚本源码文本转换为抽象语法树。Modules/:由 C 语言编写的高性能或操作系统依赖扩展模块(如_ssl.c、mathmodule.c、posixmodule.c)。Lib/:完全用 Python 语言 编写的标准库代码(如os.py、json/、urllib/等)。
三、从源码到执行:CPython 的执行管道
当开发者执行 python main.py 时,CPython 并非逐行读取文本并直接交给操作系统,而是经历了一个严密的流水线处理过程:
- 词法分析与分词(Tokenizing):
Parser/tokenizer.c将源代码文本切分为一个一个的 Token 符号(关键字、变量标识符、操作符)。 - 语法解析(Parsing):基于 PEG 语法分析器将 Token 流转换为结构化的 AST(Abstract Syntax Tree,抽象语法树)。
- 字节码编译(Compiling):编译器将 AST 遍历后生成线性的 Python 字节码,并打包封装为
PyCodeObject。如果涉及持久化,便会生成.pyc缓存文件。 - 虚拟机执行(Evaluation Loop):Python 虚拟机(PVM)分配调用栈帧(
PyFrameObject),进入核心的Python/ceval.c中的巨型 switch 循环,根据字节码指令逐一执行对应的 C 语言操作函数。
四、CPython 底层对象模型:一切皆为 PyObject
在 Python 语言中,“一切皆为对象”。而在 CPython 的 C 源码世界中,这一哲学是通过极其精炼的 C 结构体实现的。所有在 Python 中创建的变量、函数、类甚至代码块,在 C 语言内存中都以 PyObject 或其衍生体存在。
1. 定长基础对象:PyObject 结构体
在 C 语言头文件 Include/object.h 中,定义了每一个 Python 对象最起码必须包含的两个核心属性:
typedef struct _object {
_PyObject_HEAD_EXTRA // 双向链表指针,用于支持垃圾回收跟踪
Py_ssize_t ob_refcnt; // 引用计数(64位系统占 8 字节)
struct _typeobject *ob_type; // 类型指针,指向该对象所属的具体类型结构体
} PyObject;
无论是简单的整数 100 还是布尔值 True,其底层头部都强制包含引用计数 ob_refcnt 与类型指针 ob_type。类型指针决定了当调用 print()、加法 + 或属性访问时,PVM 去执行哪个具体的 C 函数指针。
2. 变长容器对象:PyVarObject 结构体
像字符串(str)、列表(list)、元组(tuple)等元素个数可变的对象,头部则使用 PyVarObject,在基础 PyObject 之上追加了一个 ob_size 字段用于记录当前容器内包含的元素数量:
typedef struct {
PyObject ob_base; // 继承自 PyObject 的头部属性
Py_ssize_t ob_size; // 容器元素数量(如列表长度、字符串字节数)
} PyVarObject;
五、CPython 的分层架构与系统机制
CPython 能够稳定运行并支持庞大的第三方扩展生态,得益于其严密的分层架构设计。从最顶层的应用代码到底层硬件接口,CPython 共划分为五个层次:
- Python 应用与纯 Python 标准库(Application & Stdlib Layer):直接使用
.py编写的高层逻辑,如http.server、dataclasses等。 - 解释器前端与编译器(Frontend & Compiler):由 C 语言编写的词法分析器、语法解析器与代码生成器,输出规范的字节码。
- 虚拟机求值引擎(Evaluation Engine / PVM):以
Python/ceval.c为核心,维护函数调用栈帧、异常处理栈与全局线程状态。 - 内存管理系统(Memory Allocator):CPython 内部实现了一套高效的三级内存分配机制(Arena -> Pool -> Block,即
PyMalloc),专门用于优化小于 512 字节的大量小对象分配,避免频繁触发系统malloc产生内存碎片。 - 系统抽象与扩展接口(Python/C API & OS Interface):提供完整的头文件与导出符号,让 NumPy、PyTorch 等第三方高性能 C/C++/CUDA 库能够直接与 Python 共享内存指针。
六、为什么 C 语言实现的 CPython 会有 GIL?
谈到 CPython 的底层实现,必然绕不开 GIL(Global Interpreter Lock,全局解释器锁)。许多初学者常误以为“Python 这门语言不支持多核并行”,但本质上,GIL 是 CPython 这一款解释器在 C 语言实现上的工程权衡,而非 Python 语言本身的语法限制。
在 C 语言中,操作一个对象的引用计数并非原子操作(Atomic Operation):
// Include/object.h 中的宏展开逻辑
#define Py_INCREF(op) ( \
_Py_INC_REFTOTAL \
((PyObject *)(op))->ob_refcnt++)
如果多个系统原生线程在没有锁保护的情况下并发对同一个 ob_refcnt 进行自增或自减,在汇编指令级别会发生读-改-写竞争冒险(Race Condition),导致内存被提前释放(产生悬垂指针 Use-After-Free)或无法释放(产生严重内存泄漏)。
CPython 团队选择在 Python/ceval.c 的解释器循环外侧加上一把互斥锁(GIL)。线程在执行 Python 字节码前必须先持有 GIL,每执行一定数量的指令(或遇到阻塞式 I/O 如文件读写、网络请求、高耗时 C 扩展计算)时主动释放 GIL,从而让渡给其他线程执行。
七、Python 与 C 的交互:C 扩展模块原理
由于 CPython 是纯 C 语言编写的,它天然具备直接加载 C/C++ 动态链接库(Linux 下为 .so,Windows 下为 .pyd)的能力。这也是 Python 能在数据分析与人工智能领域成为霸主的核心原因:业务逻辑用简洁的 Python 书写,性能瓶颈点直接用 C/C++ 或 CUDA 编写并封装为模块。
一个标准的 C 语言扩展模块通常由以下四部分组成:
- 包装函数(Wrapper Functions):接收
PyObject *args参数,使用PyArg_ParseTuple()将 Python 对象解包为 C 原生类型(如long、double),执行纯 C 运算后,通过PyLong_FromLong()等 API 将结果封包为PyObject *返回。 - 方法映射表(Method Table):声明一个
PyMethodDef数组,将 C 函数名映射为 Python 中可见的方法名。 - 模块定义体(Module Definition):声明
PyModuleDef结构体,记录模块元信息。 - 模块初始化入口(Initialization Function):导出以
PyInit_模块名命名的入口函数,供 CPython 在执行import时动态加载。
八、常见解释器实现对比与技术选型总结
在不同的生产与研究场景下,选择合适的 Python 解释器能够带来巨大的效率提升。以下是主流解释器的特性总结与决策树:
结语
回到文章开篇的核心问题:“Python 解释器是用什么语言写的?”答案是清晰且具体的:最核心、最权威的官方参考实现 CPython 是用 C 语言 编写的,并辅以 Python 语言自身 编写上层标准库。正是这种“底层 C 语言保驾底层性能与系统对接,顶层 Python 保证语法灵活优雅”的双层设计,造就了 Python 语言如今在通用开发与前沿计算领域不可动摇的技术基石地位。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









