Python 怎么调用 C++ 模块?这可是个老生常谈的问题了。为啥要这么做?还不是因为 Python 跑得慢!遇到性能瓶颈,直接用 C++ 重写,然后 Python 调用,简直完美!那到底怎么搞?别急,咱这就来盘盘道。

第一招:最直接的 ctypes

ctypes,Python 自带的“瑞士军刀”。 它的好处就是“自带”,不需要额外安装什么,开箱即用。想象一下,你写了一个 C++ 库,编译成了动态链接库(Windows 下的 .dll,Linux 下的 .so,macOS 下的 .dylib),ctypes 就能直接加载它。

“`python
import ctypes

加载动态链接库

mylib = ctypes.CDLL(‘./mylib.so’) # 注意路径,要根据实际情况修改

定义 C++ 函数的参数类型和返回类型(非常重要!)

mylib.my_cpp_function.argtypes = [ctypes.c_int, ctypes.c_double]
mylib.my_cpp_function.restype = ctypes.c_double

调用 C++ 函数

result = mylib.my_cpp_function(10, 3.14)
print(result)
“`

看到了吗?关键在于 .argtypes.restype。 它们告诉 Python,C++ 函数需要什么类型的参数,以及返回什么类型的结果。如果类型定义错了,那可就麻烦了,轻则数据错误,重则程序崩溃!

ctypes 的优点是简单粗暴,不需要额外的编译步骤。 但是,它的缺点也很明显。 你需要手动指定每个函数的参数类型和返回类型,这很繁琐,容易出错。 而且,ctypes 对 C++ 的类和对象支持不太好,如果要调用复杂的 C++ 代码,就有点力不从心了。

第二招:逼格更高的 Cython

Cython,一个“为 Python 穿上 C++ 马甲”的神器。 它是一种介于 Python 和 C++ 之间的语言,可以让你用类似 Python 的语法写 C++ 代码,然后编译成 Python 扩展模块。

首先,你需要安装 Cython:

bash
pip install cython

然后,创建一个 .pyx 文件,比如 my_module.pyx

“`python

cython: language_level=3

def my_python_function(int x, double y):
cdef double result = x * y # 定义 C++ 类型的变量
return result
“`

注意 cdef 关键字,它用来声明 C++ 类型的变量。 Cython 会把这些变量编译成 C++ 代码,从而提高性能。

接下来,创建一个 setup.py 文件:

“`python
from setuptools import setup
from Cython.Build import cythonize

setup(
ext_modules = cythonize(“my_module.pyx”)
)
“`

最后,编译 .pyx 文件:

bash
python setup.py build_ext --inplace

这会生成一个 .so 文件(或者 .pyd 文件,如果是 Windows)。 现在,你就可以像导入普通 Python 模块一样导入它了:

“`python
import my_module

result = my_module.my_python_function(10, 3.14)
print(result)
“`

Cython 的优点是性能高,语法接近 Python,可以方便地利用 C++ 的特性。 但是,它也有一定的学习曲线,需要掌握 Cython 的语法和编译过程。 再者,Cython代码的调试比纯Python代码要麻烦一些,一旦出了问题,可能需要借助GDB等工具。

第三招:更现代的 pybind11

pybind11,一个“让 C++ 和 Python 谈恋爱”的库。 它是一个轻量级的头文件库,可以让你用纯 C++ 代码编写 Python 扩展模块。

首先,你需要下载 pybind11 的头文件,并将其包含到你的 C++ 项目中。 如果你用 CMake,可以这样:

“`cmake
cmake_minimum_required(VERSION 3.15)
project(MyProject)

add_subdirectory(pybind11) # 假设 pybind11 放在项目根目录下的 pybind11 目录中

add_executable(MyModule my_module.cpp)
target_link_libraries(MyModule pybind11::module)

set_target_properties(MyModule PROPERTIES SUFFIX “.so”) # Linux

set_target_properties(MyModule PROPERTIES SUFFIX “.pyd”) # Windows

“`

然后,创建一个 C++ 文件,比如 my_module.cpp

“`cpp

include

namespace py = pybind11;

double my_cpp_function(int x, double y) {
return x * y;
}

PYBIND11_MODULE(my_module, m) {
m.doc() = “My C++ module”; // 可选的模块文档

m.def("my_cpp_function", &my_cpp_function, "A function that multiplies two numbers");

}
“`

PYBIND11_MODULE 宏定义了 Python 模块的名称,并将 C++ 函数暴露给 Python。 m.def 函数用来定义 Python 函数,它接受函数名、C++ 函数指针和文档字符串作为参数。

最后,编译 C++ 代码:

bash
mkdir build
cd build
cmake ..
make

这会生成一个 .so 文件(或者 .pyd 文件,如果是 Windows)。 现在,你就可以像导入普通 Python 模块一样导入它了:

“`python
import my_module

result = my_module.my_cpp_function(10, 3.14)
print(result)
“`

pybind11 的优点是简洁易用,性能高,可以方便地处理 C++ 的类和对象。 它还支持 C++ 11 及以上的新特性,让你可以写出更现代的 C++ 代码。 再者,由于完全使用 C++,调试体验也比Cython更好。 但缺点也很明显,你需要懂 C++,而且要熟悉 CMake 等构建工具。

总结

这三种方法各有优缺点,选择哪种取决于你的具体需求。 如果你只需要调用简单的 C++ 函数,ctypes 是一个不错的选择。 如果你需要更高的性能,或者需要处理复杂的 C++ 代码,Cython 或 pybind11 更好。 pybind11 可能是未来的趋势,因为它更现代、更易用。

当然,还有其他一些方法,比如 Boost.Python,但它们的使用不如这三种方法广泛。 希望这篇文章能帮助你解决 Python 调用 C++ 的问题!记住,实践出真知,多动手尝试,才能真正掌握这些技术。而且,别忘了根据自己的项目特点,选择最适合自己的方案,没有最好的,只有最合适的。

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。