在 python 代码中植入 c++ 代码

创建于:2025年4月12日

使用 O1 回答 Chat01

问题

在 python 代码中植入 c++ 代码

思考

Embarking on integrating C++

I’m exploring embedding C++ code within Python, considering Python's ctypes, cffi, SWIG, pybind11, and cpimport for seamless integration, with a focus on practical examples and direct code snippets.

Piecing together the flow

I'm thinking strategies to merge C++ and Python, aiming for a practical example with "pybind11" for clarity. This involves compiling and importing C++ code into Python, making the integration process more intuitive.

Reasoned about embedding C++ in Python for 4 seconds

回答

在 Python 中“直接植入”C++ 代码并不是像在同一个文件里写两种语言代码那样简单,因为 Python 解释器并不能原生执行 C++ 代码。通常来说,我们需要借助以下几种方法来让 Python 能调用 C/C++ 写的逻辑:

  1. 使用 Python 的扩展机制编译 C++ 代码
  2. 借助第三方工具/库(比如 ctypesCythonpybind11cppimport 等)
  3. 使用 SWIG 或者 cffi 将 C++ 封装为可以被 Python 调用的接口

下面给出几种常见方式的示例。


一、通过 Python 扩展机制编写 C++ 扩展

Python 官方支持编写 C/C++ 扩展模块,然后通过 import 的形式在 Python 中使用。整体思路是先将 C++ 代码编译成共享库(.so 文件或 .pyd 文件),再用 Python 导入。

1.1 目录结构

假设我们有以下文件结构:

text
my_extension/ ├── setup.py ├── mymodule.cpp └── test.py
  • mymodule.cpp:用 C++ 编写的扩展模块实现
  • setup.py:用于编译、安装扩展模块
  • test.py:测试脚本

1.2 C++ 实现(mymodule.cpp)

cpp
#include <Python.h> // 声明一个 C++ 函数,用来给 Python 调用 static PyObject* hello_from_cpp(PyObject* self, PyObject* args) { // 不需要从 args 中获取参数,这里只是返回一个字符串 return Py_BuildValue("s", "Hello from C++!"); } // 定义本模块中包含的方法表 static PyMethodDef MyMethods[] = { {"hello_from_cpp", hello_from_cpp, METH_VARARGS, "Say hello from C++"}, {NULL, NULL, 0, NULL} // 哨兵,用于结束方法表 }; // 定义模块 static struct PyModuleDef mymodule = { PyModuleDef_HEAD_INIT, "mymodule", // 模块名 "Example module that says hello from C++", -1, // 全局状态(-1表示不支持子解释器) MyMethods }; // 模块初始化函数,函数名固定为 PyInit_<模块名> PyMODINIT_FUNC PyInit_mymodule(void) { return PyModule_Create(&mymodule); }

1.3 编译安装脚本(setup.py)

python
from setuptools import setup, Extension # 定义要编译的扩展模块 module = Extension( 'mymodule', # 模块名,import 时就用这个名字 sources=['mymodule.cpp'] # 源文件 ) setup( name='mymodule', version='1.0', description='Example C++ extension for Python', ext_modules=[module], )

1.4 编译并测试

my_extension/ 目录下执行:

bash
python setup.py build

这条命令会在 build 目录生成编译后的扩展文件(通常是类似 mymodule.cpython-39-darwin.so.pyd 的文件,视操作系统/解释器版本而定)。

安装到本地 Python 环境,可以执行:

bash
python setup.py install

测试时,在 test.py 中使用:

python
import mymodule print(mymodule.hello_from_cpp())

如果输出 Hello from C++!,就说明扩展模块工作正常。


二、使用 ctypes 调用已编译的 C/C++ 共享库

如果你已经有一个现成的 C++ 动态库(如 .so.dll),或者你可以把 C++ 代码编译成动态库(也可以是 C++17/20 的代码),可以使用 Python 内置库 ctypes 来加载并调用。

2.1 编写并编译 C++ 动态库

假设我们先写一个简单的 C++ 文件 my_lib.cpp

cpp
#include <string> // 为了兼容 C 调用方式,需要 extern "C"。 extern "C" { // 假设暴露一个函数返回一个字符串 const char* get_greeting() { return "Hello from my_lib!"; } }

然后编译生成动态库(在 Linux/Mac 下):

bash
g++ -shared -fPIC my_lib.cpp -o libmylib.so

在 Windows 下则会生成 .dll

2.2 Python 端加载并调用

有了 libmylib.so(或 mylib.dll)之后,可以在 Python 中用 ctypes 调用:

python
import ctypes import os # 如果文件名是 libmylib.so,在 Linux/Mac 下这样写 lib_path = os.path.join(os.getcwd(), "libmylib.so") # Windows 下类似: lib_path = "mylib.dll" my_lib = ctypes.CDLL(lib_path) # 声明函数返回类型(如果需要) my_lib.get_greeting.restype = ctypes.c_char_p # 调用函数 greeting = my_lib.get_greeting() print(greeting.decode("utf-8"))

正常情况下会打印出 Hello from my_lib!.


三、使用 pybind11

pybind11 是一个非常简洁且流行的 C++11/14/17 绑定库,能极大简化在 Python 和 C++ 之间的调用和数据类型转换。

3.1 目录结构示例

text
my_pybind/ ├── setup.py ├── example.cpp └── test.py

3.2 在 C++ 中使用 pybind11(example.cpp)

cpp
#include <pybind11/pybind11.h> namespace py = pybind11; // 简单的 C++ 函数 int add(int i, int j) { return i + j; } PYBIND11_MODULE(example, m) { // 模块名:example m.doc() = "pybind11 example plugin"; // 模块文档 m.def("add", &add, "A function which adds two numbers"); }

3.3 setup.py

要想通过 setuptools 来编译,需要先确保系统装有 pybind11 库(可以先 pip install pybind11),再编写编译脚本:

python
import os import sys import pybind11 from setuptools import setup, Extension from setuptools.command.build_ext import build_ext # 定义扩展模块 ext_modules = [ Extension( 'example', # 生成的模块名 ['example.cpp'], include_dirs=[ pybind11.get_include() ], language='c++' ), ] setup( name='example', version='0.1', ext_modules=ext_modules, cmdclass={'build_ext': build_ext}, )

然后执行编译:

bash
python setup.py build

编译成功后,会生成 example 模块(.so.pyd 文件)。

3.4 测试(test.py)

python
import example result = example.add(3, 5) print(result) # 预期输出:8

四、使用 Cython

Cython 也是一种常见方法。它允许你在 .pyx 文件里写“近似 Python 语法”的代码,再通过 Cython 将其转为 C/C++,最后编译成扩展模块。对于需要高性能或直接调用 C/C++ 函数库的场景,Cython 也非常常用。

简要步骤:

  1. pip install cython
  2. 新建 example.pyx 文件,写含有 C 函数调用/声明的代码
  3. 通过 setup.py 配合 cythonize.pyx 编译为扩展
  4. 在 Python 中 import example 后即可调用

五、使用 cppimport(在线编译并内联调用 C++)

如果你想在 Python 文件内部“看上去写 C++ 代码”,并在导入时自动完成编译,可使用 cppimport

示例:

python
# 先 pip install cppimport import cppimport # 假设当前目录有一个 example.cpp 文件: # int add(int x, int y) { return x + y; } # 在 Python 中写一个 “.py” 文件: import cppimport example = cppimport.imp("example") # 这会自动编译并导入 example.cpp 生成的扩展 print(example.add(2, 3)) # 调用 C++ 函数,输出 5

或者甚至可以把 C++ 代码直接写在 .py 文件中,在文件开头加一个特殊的注释来让 cppimport 识别,然后自动完成“边编译边导入”,不过相对不太常见(适合快速试验)。


结论

  • 最底层的做法:编写好 C++ 代码 → 编译成动态库 → 用 ctypescffi 等方式加载。
  • 更常用的方式:写 Python 扩展(PyModule,或使用 SWIG、pybind11 等库)把 C++ 函数/类封装成 Python 可调用的模块。
  • 更高级的选择:用 pybind11Cython,能带来更简洁的接口绑定和更好的 Python/C++ 交互体验。
  • 快速试验cppimport 让你能在 Python 中直接写几行 C++ 代码并即时编译,适合小规模测试或原型。

所以,并没有一种“在 .py 文件内部直接插入 C++ 代码就能执行”的方法,而是通过上述工具或扩展机制完成“Python 调用 C++”。根据你的应用场景选择合适的方法就可以实现“在 Python 中使用 C++ 逻辑”。

分享这个问答