[AI智能体与提效-151] - Python是如何调用C语言代码?Python 调用 C 代码的四种主要方式。
表格需求场景推荐方案理由我要加速现有的 Python 代码Cython改动最小,性能提升明显,语法友好。我要调用现成的 C/C++ 库 (.so/.dll)无需重新编译库,直接在 Python 侧对接。我要开发一个新的底层高性能库pybind11(C++) 或C API(纯 C)现代项目首选 pybind11;极致控制选 C API。我只是想简单测试一下ctypes不需要安装任何东西,Python
Python 调用 C 语言代码的核心原理是:Python 解释器(通常是 CPython)本身是用 C 写的,它提供了一套标准的 C API,允许 C 代码作为“扩展模块”被加载到 Python 进程中执行。
这就好比 Python 是一个经理,C 语言是技术专家。经理(Python)可以通过特定的“内部电话”(C API)直接指挥专家(C)干活,甚至让专家直接操作内存。
以下是 Python 调用 C 代码的四种主要方式,按从底层到高层的顺序排列:
1. Python C API (最底层、最原生)
这是 Python 官方提供的标准接口。你需要编写符合 Python 规范的 C 代码,将其编译成动态链接库(.so Linux/Mac 或 .pyd Windows),然后像导入普通 Python 模块一样导入。
- 原理:
- C 代码中包含
#include <Python.h>。 - 定义函数时,必须遵循特定格式(如
static PyObject* my_func(PyObject *self, PyObject *args))。 - 需要手动处理引用计数(管理内存)和类型转换(把 Python 的
int转为 C 的long,反之亦然)。
- C 代码中包含
- 优点:性能最高,无中间层开销;可以深度操作 Python 内部对象。
- 缺点:开发难度大,代码繁琐,容易因内存管理错误导致崩溃(Segmentation Fault)。
- 适用场景:编写高性能的基础库(如 NumPy, TensorFlow 的底层)。
代码示例逻辑:
// hello.c
#include <Python.h>
static PyObject* say_hello(PyObject* self, PyObject* args) {
const char* name;
// 解析 Python 传入的参数
if (!PyArg_ParseTuple(args, "s", &name))
return NULL;
printf("Hello %s!\n", name);
// 返回 Python 对象 (None)
Py_RETURN_NONE;
}
// 定义模块方法表
static PyMethodDef Methods[] = {
{"say_hello", say_hello, METH_VARARGS, "Print a greeting"},
{NULL, NULL, 0, NULL}
};
// 定义模块结构
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
"hello_module",
NULL,
-1,
Methods
};
// 模块初始化入口
PyMODINIT_FUNC PyInit_hello_module(void) {
return PyModule_Create(&moduledef);
}
编译后在 Python 中:import hello_module; hello_module.say_hello("World")
2. Cython (最流行、平衡性最好)
Cython 是 Python 的超集。你写的是类似 Python 的代码(.pyx 文件),但它可以添加 C 类型的声明。Cython 编译器会将这些代码翻译成优化的 C 代码,然后再编译成 Python 扩展模块。
- 原理:
- 语法接近 Python,但可以声明
cdef int x这样的 C 变量。 - 自动处理 Python 对象与 C 数据的转换。
- 生成 C 代码并调用 gcc/clang 编译。
- 语法接近 Python,但可以声明
- 优点:语法简单(90% 是 Python 语法),性能提升巨大(接近 C),生态成熟。
- 缺点:需要学习少量的额外语法(
cdef,cpdef),构建过程稍复杂。 - 适用场景:加速 Python 循环、数值计算、封装现有的 C 库。
代码示例逻辑:
python
# hello.pyx
def say_hello(str name):
# 这一行会被编译成高效的 C 代码
print(f"Hello {name}!")
# 纯 C 级别的循环加速
cdef int i
for i in range(1000000):
pass
3. ctypes / cffi (无需编译 C 代码,直接调用现有库)
如果你已经有一个编译好的 C 动态库(.dll, .so),不想重新编译,只想在 Python 里调用它,就用这两个。
- ctypes (标准库自带):
- 原理:在 Python 运行时动态加载库,手动定义参数类型和返回类型。
- 优点:无需安装额外包,Python 内置。
- 缺点:类型定义繁琐,出错时调试困难。
- cffi (第三方库):
- 原理:类似 ctypes,但语法更优雅,支持内联 C 代码声明。
- 优点:比 ctypes 更易用,性能略好,PyPy 支持更好。
ctypes 示例逻辑:
python
import ctypes
# 加载已编译好的 C 库
lib = ctypes.CDLL('./libhello.so')
# 告诉 Python 函数的参数类型和返回类型
lib.say_hello.argtypes = [ctypes.c_char_p]
lib.say_hello.restype = None
# 调用
lib.say_hello(b"World")
4. pybind11 (现代 C++ 首选,也适用于 C)
虽然名字叫 pybind11(主要针对 C++),但它也可以用来封装 C 代码(需包裹一层 C++)。它是现代 C++ 项目连接 Python 的事实标准。
- 原理:利用 C++ 模板元编程,自动生成绑定代码。
- 优点:语法极其简洁现代,自动处理异常和类型转换,头文件-only(只需包含头文件即可使用)。
- 缺点:主要针对 C++,纯 C 项目需要稍微包装一下。
总结:如何选择?
表格
| 需求场景 | 推荐方案 | 理由 |
|---|---|---|
| 我要加速现有的 Python 代码 | Cython | 改动最小,性能提升明显,语法友好。 |
| 我要调用现成的 C/C++ 库 (.so/.dll) | ctypes / cffi | 无需重新编译库,直接在 Python 侧对接。 |
| 我要开发一个新的底层高性能库 | pybind11 (C++) 或 C API (纯 C) | 现代项目首选 pybind11;极致控制选 C API。 |
| 我只是想简单测试一下 | ctypes | 不需要安装任何东西,Python 自带。 |
核心流程图解
无论用哪种方法,数据流向都是:
- Python 层:用户调用函数
func(arg)。 - 桥接层 (Cython/ctypes/C API):
- 将 Python 对象(如
list,str)解包 (Unmarshal) 为 C 的基本数据类型(如int*,char*)。 - 检查类型是否匹配。
- 将 Python 对象(如
- C 层:执行纯粹的 C 逻辑(操作内存、指针运算、硬件交互)。
- 桥接层:将 C 的返回结果打包 (Marshal) 回 Python 对象。
- Python 层:接收返回值,继续执行后续 Python 代码。
这种机制使得 Python 能够兼具开发的灵活性(上层逻辑)和执行的效率/硬件能力(底层 C 代码)。
更多推荐



所有评论(0)