C++编写Python扩展模块是提升Python程序性能、复用现有C++代码库的常见方案,目前主流实现方式分为传统Python C API和第三方封装库两种,其中pybind11因语法简洁、学习成本低被广泛使用。

两种实现方式对比
在选择实现方案前,首先了解两种主流方式的差异,方便根据项目需求选择:
| 方案类型 | 优势 | 劣势 | 适用场景 |
|---|---|---|---|
| Python C API | 无需额外依赖,官方原生支持 | 语法繁琐,手动管理引用计数容易出错 | 无第三方库依赖要求的小型模块 |
| pybind11 | 语法接近C++原生,自动管理引用,兼容C++11及以上标准 | 需要引入第三方库 | 复杂业务模块、需要大量C++特性支持的场景 |
基于pybind11的实现步骤
环境准备
首先安装pybind11库,推荐使用pip安装,同时需要确保本地已安装C++编译环境,Windows系统需要安装Visual Studio,Linux系统需要安装gcc,macOS系统需要安装Xcode命令行工具。
# 安装pybind11
pip install pybind11
# 查看Python头文件路径,后续编译需要
python -c "import sysconfig; print(sysconfig.get_path('include'))"
编写C++扩展代码
下面编写一个简单的扩展模块,实现两个功能:一个加法函数,一个存储数据的简单类。
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
namespace py = pybind11;
// 普通函数,计算两个整数之和
int add(int a, int b) {
return a + b;
}
// 定义一个简单的C++类
class DataProcessor {
private:
int data;
public:
DataProcessor(int init_val) : data(init_val) {}
// 设置数据
void set_data(int new_val) {
data = new_val;
}
// 获取数据
int get_data() const {
return data;
}
// 数据翻倍
int double_data() {
data *= 2;
return data;
}
};
// 模块定义,模块名为my_cpp_module
PYBIND11_MODULE(my_cpp_module, m) {
m.doc() = "这是一个用C++编写的Python扩展模块"; // 模块文档
// 注册加法函数
m.def("add", &add, "计算两个整数的和",
py::arg("a"), py::arg("b"));
// 注册DataProcessor类
py::class_<DataProcessor>(m, "DataProcessor")
.def(py::init<int>(), "初始化处理器,传入初始值")
.def("set_data", &DataProcessor::set_data, "设置存储的数据")
.def("get_data", &DataProcessor::get_data, "获取存储的数据")
.def("double_data", &DataProcessor::double_data, "将数据翻倍并返回");
}
编译扩展模块
编写好代码后,需要将其编译为Python可识别的动态链接库,Windows下生成.pyd文件,Linux/macOS下生成.so文件。
可以使用pybind11提供的编译工具简化编译流程,创建一个setup.py文件:
from setuptools import setup, Extension
import pybind11
# 定义扩展模块
ext_module = Extension(
'my_cpp_module',
sources=['my_cpp_ext.cpp'], # 你的C++源码文件名
include_dirs=[pybind11.get_include()],
language='c++'
)
setup(
name='my_cpp_module',
version='1.0',
description='C++编写的Python扩展模块',
ext_modules=[ext_module]
)
在终端执行以下命令完成编译:
# 编译并安装模块到当前Python环境 python setup.py install
测试扩展模块
编译完成后,就可以在Python中直接导入使用这个模块了:
import my_cpp_module
# 测试加法函数
result = my_cpp_module.add(3, 5)
print(f"3 + 5 = {result}")
# 测试DataProcessor类
processor = my_cpp_module.DataProcessor(10)
print(f"初始数据: {processor.get_data()}")
processor.double_data()
print(f"翻倍后数据: {processor.get_data()}")
processor.set_data(20)
print(f"设置新数据后: {processor.get_data()}")
传统Python C API实现示例
如果不想依赖第三方库,也可以使用Python原生的C API实现扩展,下面是相同功能的实现代码:
#include <Python.h>
// 加法函数实现
static PyObject* py_add(PyObject* self, PyObject* args) {
int a, b;
// 解析Python传入的参数
if (!PyArg_ParseTuple(args, "ii", &a, &b)) {
return NULL;
}
int result = a + b;
// 将结果转换为Python整数对象返回
return PyLong_FromLong(result);
}
// 模块方法列表
static PyMethodDef MyCppMethods[] = {
{"add", py_add, METH_VARARGS, "计算两个整数的和"},
{NULL, NULL, 0, NULL} // 结束标记
};
// 模块定义
static struct PyModuleDef my_cpp_module = {
PyModuleDef_HEAD_INIT,
"my_cpp_module", // 模块名
"原生C API实现的Python扩展模块", // 模块文档
-1,
MyCppMethods
};
// 模块初始化函数
PyMODINIT_FUNC PyInit_my_cpp_module(void) {
return PyModule_Create(&my_cpp_module);
}
这种方式的编译需要指定Python的头文件路径和库路径,编译命令示例(Linux环境):
gcc -shared -fPIC -I$(python3 -c "import sysconfig; print(sysconfig.get_path('include'))") my_cpp_ext.c -o my_cpp_module.so -lpython3.8
注意事项
- C++编译标准需要匹配pybind11的要求,建议至少使用C++11标准,编译时添加
-std=c++11参数 - 使用Python C API时一定要注意引用计数管理,避免内存泄漏或者野指针问题
- 如果扩展中需要使用numpy等库的数据结构,pybind11提供了对应的
pybind11/numpy.h头文件支持 - 不同Python版本的C API存在细微差异,编写原生扩展时需要做好版本兼容处理