python extending with c++
Python调用C++的三种主流方案:直接基于PythonCAPI编写扩展模块性能最优但开发复杂;借助Boost.Python库封装可简化代码;使用ctypes加载动态库无需修改C++源码。每种方案适用于不同场景。
Python 调用 C++:三种主流方案详解
Python 和 C++ 的混合编程在工程实践中并不少见。有时是为了复用已有的底层库,有时是看中 C++ 的执行效率,又或者单纯想用 Python 的灵活性来调度 C++ 的逻辑。不管出于哪种动机,实现 Python 调用 C++ 的路径其实相当清晰,关键在于选对工具。
目前主流的方式有三种:直接基于 Python C API 写扩展模块、借助 Boost.Python 库进行封装、以及使用 ctypes 在运行时加载动态库。每种方案都有其适用场景,下面逐一展开。
1. 基于 Python C API 的方案
这是最基础的做法,也是理解 Python 扩展机制的最佳入口。它的核心思路是:用 C++ 实现一个包装函数,通过 PyArg_ParseTuple 解析 Python 传入的参数,调用真正的 C++ 逻辑,最后用 Py_BuildValue 将结果返回给 Python。
看一个具体例子。假设有一个 TestFact 类,它封装了阶乘计算的成员函数 fact。我们需要在 C++ 端编写一个包装函数 wrap_fact,并将其注册到 Python 解释器中。

#include
class TestFact {
public:
TestFact(){};
~TestFact(){};
int fact(int n);
};
int TestFact::fact(int n) {
if (n <= 1)
return 1;
else
return n * (n - 1);
}
int fact(int n) {
TestFact t;
return t.fact(n);
}
PyObject* wrap_fact(PyObject* self, PyObject* args) {
int n, result;
if (!PyArg_ParseTuple(args, "i:fact", &n))
return NULL;
result = fact(n);
return Py_BuildValue("i", result);
}
static PyMethodDef exampleMethods[] = {
{"fact", wrap_fact, METH_VARARGS, "Caculate N!"},
{NULL, NULL}
};
extern "C"
void initexample() {
PyObject* m;
m = Py_InitModule("example", exampleMethods);
}
将上述代码保存为 wrapper.cpp,然后编译成动态库:
g++ -fPIC wrapper.cpp -o example.so -shared -I/usr/include/python2.6 -I/usr/lib/python2.6/config
编译完成后,进入 Python 即可直接调用:
import example
example.fact(4)
这套方案的优点是完全控制底层细节,不依赖第三方库;缺点是样板代码较多,每暴露一个函数都要手写包装和类型转换,适合对性能或模块体积有严格要求的场景。
2. 借助 Boost.Python 封装
如果讨厌手动编写 PyArg_ParseTuple 和 PyMethodDef 这类重复劳动,Boost.Python 是很理想的替代品。它允许直接用 C++ 代码定义 Python 模块,甚至可以封装类及其成员函数。
先安装开发环境:
apt-get install libboost-python-dev
之后编写一个简单的示例:
#include
char const* greet() {
return "hello, world";
}
BOOST_PYTHON_MODULE(hello) {
using namespace boost::python;
def("greet", greet);
}
编译时需要指定 Python 和 Boost 的路径,以及链接对应的库。可参考如下的 Makefile:
PYTHON_VERSION = 2.7
PYTHON_INCLUDE = /usr/include/python$(PYTHON_VERSION)
BOOST_INC = /usr/include
BOOST_LIB = /usr/lib
TARGET = hello
$(TARGET).so: $(TARGET).o
g++ -shared -Wl,--export-dynamic $(TARGET).o -L$(BOOST_LIB) -lboost_python -L/usr/lib/python$(PYTHON_VERSION)/config-x86_64-linux-gnu -lpython$(PYTHON_VERSION) -o $(TARGET).so
$(TARGET).o: $(TARGET).cpp
g++ -I$(PYTHON_INCLUDE) -I$(BOOST_INC) -fPIC -c $(TARGET).cpp
运行 make 后,在 Python 中使用:
import hello
hello.greet()
值得一提的是,Boost.Python 的库名可能因版本而异,比如 lboost_python-gcc42-mt-1_34_1。如果不确定具体名称,可以到 /usr/lib 目录下查找匹配的实际文件。
3. 用 ctypes 完成轻量调用
如果不想写任何 C++ 包装代码,ctypes 是最省事的方案。它能够直接在 Python 中加载动态库并调用其中的函数,所有类型转换由 ctypes 自动处理。
我们仍以 TestFact 为例,只需要在 C++ 端暴露一个 C 接口:
#include
class TestFact {
public:
TestFact(){};
~TestFact(){};
int fact(int n);
};
int TestFact::fact(int n) {
if (n <= 1)
return 1;
else
return n * (n - 1);
}
extern "C"
int fact(int n) {
TestFact t;
return t.fact(n);
}
同样编译成 example.so:
g++ -fPIC wrapper.cpp -o example.so -shared -I/usr/include/python2.6 -I/usr/lib/python2.6/config
然后在 Python 中加载并调用:
import ctypes
pdll = ctypes.CDLL('/home/ubuntu/tmp/example.so')
pdll.fact(4)
ctypes 不需要任何 Python C API 代码,也不依赖 Boost 这类重型库,特别适合快速验证或集成已有的 C/C++ 库。但如果需要暴露复杂的 C++ 类或需要异常处理,它的局限性就会显现出来。
总结一下:从底层控制力来看,Python C API 最强;从开发效率来看,Boost.Python 更便捷;从零依赖快速集成来看,ctypes 最轻量。实际项目中选哪种,取决于你对性能、可维护性和开发速度的权衡。
Windows 10 是一款微软推出的经典操作系统,拥有硬件兼容性与多任务处理能力。它更偏向把系统状态查看和常用调节动作放在一起,适合需要持续观察和微调设备状态的场景。
极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。
















