C/C++调用Python函数(CodeBlocks平台实现)

因需,博主需要编写C/C++程序,在其中调用Python编写的相关函数,故此,整理了C/C++调用Python函数的相关方法

对于Python的版本,提及了常用的 Python2.7 与 Python3.5 版本,其余常用的 3.x 版本的使用方法与 3.5 版本的类似,区别将会在教程中点明

对于调用的Python函数,可以是无参的或有参的,可以是有返回值的或是无返回值的

CodeBlocks 环境配置

该部分借鉴了 C++程序调用Python的函数(简单应用)及Ubuntu16.04下codeblocks的环境配置 一文,对其进行了一定的补充说明

该部分针对Ubuntu系统实现,对于其余系统(包括Windows),差别仅仅是Python相关文件的所在位置不同,可手动搜索其存储位置,再按本教程的配置方法实现

针对Python环境的配置,需要知道Python的相关文件位置,可通过以下命令查找Python的相关文件所在

whereis python

在这里插入图片描述
该部分的环境配置是基于某一工程实现,因此需要建立工程,再对其进行设置

在建立的工程名上鼠标右键点击,在弹出的选项中选择 Build options…

在这里插入图片描述
而后,在新弹出的窗口中选择 Search directories --> Compiler --> Add --> ,步骤如图

在这里插入图片描述
在文件系统中选择 /usr/include/python3.5 文件夹,其中 python3.5 可以根据需要换成其他版本,但该文件必须是 include 类型文件,选择确认后会有一个 Keep this as a relative path? 问题,选择 ,而后在 Add directory 窗口点击确定,结果如图

在这里插入图片描述
同样在 Search directories 中,选择 Linker,同样添加相关文件 /usr/lib/python3.5,与之前相同,文件版本可替换,但 CompilerLinker 选择的版本必须一致

在这里插入图片描述
在这里插入图片描述
而后,在同一窗口中,选择 Linker settings,在其中的 Link Libiaries 中添加文件 /usr/lib/python3.5/config-3.5m-x86_64-linux-gnu/libpython3.5.so ,注意点与之前一致,确认后结果如图

在这里插入图片描述
在这里插入图片描述
最后,点击 Project build options 窗口的 确定 按钮,保存环境配置

调用Python函数

该部分教程仅使用了C/C++与Python自设的调用方法,并未使用第三方软件实现,该方法在官网中亦有提及,但官网的描述细节部分不足,直接运行其提供的代码会有报错,仅供参考(官网网址:Python 3.5.9 documentation,其中可选择其他版本的帮助文档)

本教程提供的方法同时适用于Python3与Python2.7版本,版本不同而需要修改的一些细节会在方法中点明

程序文件列表

在CodeBlocks中建立了一个名为 Python3 的工程,下图是该工程中的文件列表,其中 main.cpp 为编写的C++程序, test.py 为Python程序,其余文件为工程自动创建或运行后自动创建

在这里插入图片描述

Python程序

文件 test.py 中内容如下,仅包含四个函数,有无参数与有无返回值的区别

在这里插入图片描述

C/C++程序

1、头文件部分

需要添加 #include “python3.5/Python.h” 头文件,其中 python3.5 部分可以修改为需要的版本

//C程序头文件
#include <stdio.h>
#include <string.h>  //需要用到字符串函数
#include "python3.5/Python.h"
//C++程序头文件
#include <iostream>
#include "python3.5/Python.h"
using namespace std;

2、Python参数类型

该部分为C/C++程序中使用的Python数据

PyObject *pName, *pModule, *pFunc, *pArgs, *pValue;

3、Python调用初始化与资源释放

Python资源的初始化与释放是一对操作

Py_Initialize();  //初始化
……
Py_Finalize();  //释放资源

4、Python模块目录切换

其中 char * pathstring path 是根据博主自己的模块目录填写

char * cstr_cmdstring chdir_cmd 将模块目录加入系统的操作是博主搜索了众多博客,最终找到的可以成功运行的方法

C程序与C++程序在该部分有所差别,C语言中并未提供 string 类型,因此需要使用 char* 类型代替,若不熟练,在该步骤中使用 char* 指针可能会有理解上的偏差,可参考 由strcat函数引发的C语言中数组和指针问题的思考 便于理解

//C程序部分
//将Python工作路径切换到待调用模块所在目录
char *path = "/home/wjh/桌面/Python3";
char *str = "sys.path.insert(0,\"";
char *chdir_cmd = (char*)malloc(strlen(path) + strlen(str) + 1);
strcat(strcpy(chdir_cmd, str), path);
str = "\")";
char *cstr_cmd = (char*)malloc(strlen(chdir_cmd) + strlen(str) + 1);
strcat(strcpy(cstr_cmd, chdir_cmd), str);
PyRun_SimpleString("import sys");
PyRun_SimpleString(cstr_cmd);
//C++程序部分
//将Python工作路径切换到待调用模块所在目录
string path = "/home/wjh/桌面/Python3";
string chdir_cmd = string("sys.path.insert(0,\"") + path + "\")";
const char* cstr_cmd = chdir_cmd.c_str();
PyRun_SimpleString("import sys");
PyRun_SimpleString(cstr_cmd);

5、调用模块

其中 PyUnicode_DecodeFSDefault(“test”) 中的 test 是模块名,即编写Python程序时import之后的名称,本教程中,博主编写了一个Python程序直接调用,因此该程序的模块名即为其文件名

以下代码中,因该教程涉及的模块调用方法较为简单,注释部分的代码等同于以上三行代码,具体的差别可自行查看官方帮助文档,可在其中搜索相关函数查看具体描述

pName = PyUnicode_DecodeFSDefault("test");  //模块名
pModule = PyImport_Import(pName);  //调用模块
Py_DECREF(pName);
//pModule = PyImport_ImportModule("test");  //调用模块

以上代码适用于Python3版本,Python2.7版本需要将第一行代码修改为如下函数,其余部分保持不变(包括注释的代码,其效用仍旧等同于修改后的三行代码)

pName = PyString_FromString("test");  //模块名

6、调用函数

其中 pModule 为Python模块,“hello” 为需要调用的函数名

pFunc = PyObject_GetAttrString(pModule, "hello");  //调用函数

7、函数参数设置

PyTuple_New 函数用于设置需要调用的Python函数的参数个数
PyLong_FromLong 函数设置参数值,设置的参数类型为long,若需要设置其他类型的参数,可查阅官方文档获取相关函数
PyTuple_SetItem 函数用于将设置的参数值放入参数元组中,其中 pArgs 为参数元组,pValue 为参数值,中间的数字代表该参数值为第几个参数(从0开始)

pArgs = PyTuple_New(2);  //参数个数
pValue = PyLong_FromLong(1);  //参数值来源
PyTuple_SetItem(pArgs, 0, pValue);  //参数设置
pValue = PyLong_FromLong(2);  //参数值来源
PyTuple_SetItem(pArgs, 1, pValue);  //参数设置

8、函数执行

函数执行涉及两个函数 PyEval_CallObjectPyObject_CallObject ,其中 PyEval_CallObject 用于无参数无返回值的Python函数,其余情况使用 PyObject_CallObject 函数

其中 pFunc 为需要调用的Python函数,NULL 表示无参数,pArgs 表示函数参数,pValue 为函数返回值

PyEval_CallObject(pFunc, NULL);  //调用无参数无返回值的Python函数
pValue = PyObject_CallObject(pFunc, NULL);  //调用无参数有返回值的Python函数
PyObject_CallObject(pFunc, pArgs);  //调用有参数无返回值的Python函数
pValue = PyObject_CallObject(pFunc, pArgs);  //调用有参数有返回值的Python函数

9、返回值获取

该部分涉及的函数较多,且Python3与Python2.7版本的函数存有差别,具体的使用需要查阅官方文档提供的函数介绍

以下命令以 long 类型为例,适用于Python的各个版本,其中 pValue 为函数执行的返回值

//PyLong_AsLong(pValue)
printf("%ld\n\n", PyLong_AsLong(pValue));
cout << PyLong_AsLong(pValue) << endl << endl;

10、完整代码

该部分代码包含了以上所有内容,调用了四类不同的Python函数,但需要注意的是,该代码使用 C++语言 实现,适用于 Python3.5,其余语言版本需要对其进行一点修改

#include <iostream>
#include "python3.5/Python.h"

using namespace std;

int main() {

    PyObject *pName, *pModule, *pFunc, *pArgs, *pValue;

    Py_Initialize();  //初始化

    //将Python工作路径切换到待调用模块所在目录
    string path = "/home/wjh/桌面/Python3";
    string chdir_cmd = string("sys.path.insert(0,\"") + path + "\")";
    const char* cstr_cmd = chdir_cmd.c_str();
    PyRun_SimpleString("import sys");
    PyRun_SimpleString(cstr_cmd);

    pName = PyUnicode_DecodeFSDefault("test");  //模块名
    pModule = PyImport_Import(pName);  //调用模块
    Py_DECREF(pName);
    //pModule = PyImport_ImportModule("test");  //调用模块

    //1 无参数无返回
    cout << "1 无参数无返回" << endl;
    pFunc = PyObject_GetAttrString(pModule, "hello");  //调用函数
    PyEval_CallObject(pFunc, NULL);  //调用无参数无返回值的Python函数
    cout << endl;

    //2 无参数有返回
    cout << "2 无参数有返回" << endl;
    pFunc = PyObject_GetAttrString(pModule, "add");  //调用函数
    pValue = PyObject_CallObject(pFunc, NULL);
    cout << PyLong_AsLong(pValue) << endl << endl;

    //3 有参数无返回
    cout << "3 有参数无返回" << endl;
    pFunc = PyObject_GetAttrString(pModule, "sub");  //调用函数
    pArgs = PyTuple_New(2);  //参数个数
    pValue = PyLong_FromLong(1);  //参数值来源
    PyTuple_SetItem(pArgs, 0, pValue);  //参数设置
    pValue = PyLong_FromLong(2);  //参数值来源
    PyTuple_SetItem(pArgs, 1, pValue);  //参数设置
    PyObject_CallObject(pFunc, pArgs);
    cout << endl;

    //4 有参数有返回
    cout << "4 有参数有返回" << endl;
    pFunc = PyObject_GetAttrString(pModule, "Add");  //调用函数
    pArgs = PyTuple_New(2);  //参数个数
    pValue = PyLong_FromLong(1);  //参数值来源
    PyTuple_SetItem(pArgs, 0, pValue);  //参数设置
    pValue = PyLong_FromLong(2);  //参数值来源
    PyTuple_SetItem(pArgs, 1, pValue);  //参数设置
    pValue = PyObject_CallObject(pFunc, pArgs);
    cout << PyLong_AsLong(pValue) << endl << endl;

    Py_Finalize();

    return 0;
}

执行结果(C与C++程序执行结果完全相同)
在这里插入图片描述
11、备注说明

C/C++调用Python函数提供了许多判断某一步骤是否出错的函数,类似Java中try与catch涉及的函数操作,具体部分仍旧可参考官方文档,本教程仅提供常用的一些判断操作

检查初始化是否成功

if (!Py_IsInitialized()) {
	return 1;
}

判断模块是否调用成功

if (pModule != NULL) {
    //调用成功
} else {
    PyErr_Print();  //错误打印到命令行
    return 1;
}

判断函数是否调用成功,其中使用了 Py_XDECREF() 解除Python对象的引用,以便回收

if (pFunc && PyCallable_Check(pFunc)) {
	//调用成功
} else {
	if (PyErr_Occurred())
    	PyErr_Print();  //错误打印到命令行
}
Py_XDECREF(pFunc);
Py_DECREF(pModule);

判断函数是否获得返回值

if (pValue != NULL) {
	printf("Result of call: %ld\n", PyLong_AsLong(pValue));
	Py_DECREF(pValue);
} else {
	Py_DECREF(pFunc);
    Py_DECREF(pModule);
    PyErr_Print();
    return 1;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值