深入浅析 C++ 调用 Python 模块

  • Post category:Python

当我们需要在C++程序中调用Python模块时,可以使用Python提供的C API,通过这个API,我们可以在C++代码中调用Python解释器,并在C++程序中直接调用Python模块中的函数和方法。

下面,我们将详细讲解如何在C++程序中调用Python模块,并附上两个示例。

安装Python解释器

在开始之前,我们需要安装Python解释器。可以从Python官网上下载适合自己操作系统的Python解释器并安装。

编写C++代码

接下来,我们将编写C++代码,以便在C++程序中调用Python模块。该代码将涉及到Python.h头文件,因此在程序中使用时需要确保已经安装了Python解释器。

#include <Python.h>

int main() {
    // 初始化Python解释器
    Py_Initialize();

    // 加载模块
    PyObject* moduleName = PyUnicode_FromString("my_module");
    PyObject* module = PyImport_Import(moduleName);

    if (module) {
        // 在模块中找到函数
        PyObject* methodName = PyUnicode_FromString("my_function");
        if (PyObject_HasAttr(module, methodName)) {
            PyObject* function = PyObject_GetAttr(module, methodName);

            // 调用函数
            PyObject_CallObject(function, nullptr);
            Py_DECREF(function);
        }
        Py_DECREF(methodName);
    }
    Py_XDECREF(moduleName);
    Py_Finalize();
    return 0;
}

上面的代码主要实现了两个功能:初始化Python解释器和调用Python模块中的函数。

在初始化Python解释器后,我们使用PyUnicode_FromString从C++字符串中获取Python模块的名称,然后使用PyImport_Import函数加载模块。如果加载成功,我们可以使用PyObject_HasAttr和PyObject_GetAttr函数在模块中查找我们要调用的函数。在找到函数后,我们可以使用PyObject_CallObject函数直接调用它。

值得注意的是,我们在程序中使用了Py_DECREF和Py_XDECREF来释放我们创建的对象。这是因为使用Python C API时,我们需要手动管理内存。在使用完对象后,务必正确地释放它们,否则可能会导致内存泄漏或者崩溃。

示例一:使用C++调用Python标准库的random模块生成随机数

#include <Python.h>
#include <iostream>

int main() {
    // 初始化Python解释器
    Py_Initialize();

    // 加载模块
    PyObject* moduleName = PyUnicode_FromString("random");
    PyObject* module = PyImport_Import(moduleName);

    if (module) {
        // 在模块中找到函数
        PyObject* methodName = PyUnicode_FromString("randint");
        if (PyObject_HasAttr(module, methodName)) {
            PyObject* function = PyObject_GetAttr(module, methodName);

            PyObject* args = PyTuple_New(2);
            PyTuple_SetItem(args, 0, PyLong_FromLong(1));
            PyTuple_SetItem(args, 1, PyLong_FromLong(10));

            PyObject* result = PyObject_CallObject(function, args);

            std::cout << "Random number: " << PyLong_AsLong(result) << std::endl;

            Py_DECREF(result);
            Py_DECREF(args);
            Py_DECREF(function);
        }
        Py_DECREF(methodName);
    }
    Py_XDECREF(moduleName);
    Py_Finalize();
    return 0;
}

该示例使用C++调用Python标准库的random模块,生成一个范围在1到10之间的随机数。在代码中,我们使用PyTuple_New和PyTuple_SetItem函数创建了一个元组,用作传递给randint方法的参数。在调用完Python函数后,我们利用PyLong_AsLong函数将Python对象转化为C++中的long类型。

示例二:使用C++调用自定义的Python模块

下面,我们来看一个使用C++调用自定义的Python模块的示例。

首先,我们需要编写一个Python模块,如下面的my_module.py所示:

def my_function():
    print('Hello, world!')

def double(x: float) -> float:
    return x * 2

在该模块中,我们定义了两个函数:my_function和double,前者用于输出一条Hello, world!的信息,后者用于将输入的参数翻倍。下面是对应的C++代码:

#include <Python.h>
#include <iostream>

int main() {
    // 初始化Python解释器
    Py_Initialize();

    // 加载模块
    PyObject* moduleName = PyUnicode_FromString("my_module");
    PyObject* module = PyImport_Import(moduleName);

    if (module) {
        // 在模块中找到函数
        PyObject* methodName1 = PyUnicode_FromString("my_function");
        if (PyObject_HasAttr(module, methodName1)) {
            PyObject* function1 = PyObject_GetAttr(module, methodName1);

            // 调用函数
            PyObject_CallObject(function1, nullptr);

            Py_DECREF(function1);
        }
        Py_DECREF(methodName1);

        // 在模块中找到函数
        PyObject* methodName2 = PyUnicode_FromString("double");
        if (PyObject_HasAttr(module, methodName2)) {
            PyObject* function2 = PyObject_GetAttr(module, methodName2);

            PyObject* args = PyTuple_New(1);
            PyTuple_SetItem(args, 0, PyFloat_FromDouble(2.5));

            PyObject* result = PyObject_CallObject(function2, args);
            std::cout << "Result: " << PyFloat_AsDouble(result) << std::endl;

            Py_DECREF(result);
            Py_DECREF(args);
            Py_DECREF(function2);
        }
        Py_DECREF(methodName2);
    }
    Py_XDECREF(moduleName);
    Py_Finalize();
    return 0;
}

在上面的代码中,我们首先使用Py_Initialize函数初始化Python解释器。然后,使用PyUnicode_FromString函数创建Python模块my_module的名称,并通过PyImport_Import函数将模块加载进来。在模块加载成功后,我们使用PyObject_HasAttr和PyObject_GetAttr查找我们要调用的函数,并使用PyObject_CallObject函数执行它们。在调用函数时,我们使用PyTuple_New和PyTuple_SetItem函数创建一个元组对象,用来表示调用函数时传递的参数。将函数调用的结果释放后,我们利用Py_DECREF函数释放创建的对象。

上面这个例子中,我们调用了my_module.py中的double函数,并传递了一个python float对象作为参数,函数将该参数翻倍,最终返回一个python float对象。在C++中,我们使用PyFloat_FromDouble函数将C++的double类型转换为python float类型,并使用PyFloat_AsDouble函数将python float对象转换为C++的double类型。