C/C++调用Python研究记录

First Post:

Last Update:

Word Count:
934

Read Time:
3 min

前言

在开发过程中,遇到了C/C++实现起来很麻烦的问题,于是就想到了利用Python来完成这部分工作,在这里记录下连接开发的过程和踩到的坑。

开始使用

Python/C API官方中文文档

Cmake引用

首先,使用者和开发者都必须安装Python,而且版本要相对,开发者用的Python3.10,用户也必须使用Python3.10,若开发使用3.10,用户使用3.9,那是会报错找不到dll/so的。
在cmake项目中,引入python只需要以下两行:

CMAKE 共 2 行
展开
1
2
find_package(Python3 COMPONENTS Development REQUIRED) # 找到python的库
target_link_libraries(main PRIVATE Python3::Python) # 连接python

这里有一个示例:
CMAKE 共 10 行
展开
1
2
3
4
5
6
7
8
9
10
cmake_minimum_required(VERSION 3.21) # 项目要求的最低cmake版本
project(main) # 项目的名称

set(CMAKE_CXX_STANDARD 17) # 设置c++版本

find_package(Python3 COMPONENTS Development REQUIRED) # 找到python的库

add_executable(main main.cpp) # 生成可执行文件

target_link_libraries(main PRIVATE Python3::Python) # 连接python

重新加载cmake缓存后,python就已经被包含进项目中了。要是报错了,检查以下Python的安装吧ο(=•ω<=)ρ⌒☆

项目使用

在C/C++使用的时候,必须引入的头文件是Python.h,这是使用python语言的关键。
因为这是个C库,所以引入之后不需要using namespace这种操作。
接下来,需要认识几个函数的作用。

C++ 共 13 行
展开
1
2
3
4
5
6
7
8
9
10
11
12
13
Py_Initialize(); // 初始化Python环境
Py_Finalize(); // 释放python所占用的内存空间
PyRun_SimpleString(char * str); // 执行一段Python代码
Py_BuildValue(const char *format, ...); // 创建一个python类型的变量
PyList_New(int size); // 创建一个新的python队列
PyList_SetItem(PyObject * list, int pos, PyObject * value); // 向队列中的指定位置插入数据
PyTuple_New(int size); // 创建一个Python元组
PyTuple_SetItem(PyObject * tuple, int pos, PyObject * value); // 向元组中的指定位置插入数据
PyImport_ImportModule(const char *name); // 导入py或者pyd文件
PyObject_GetAttrString(PyObject * module, const char * function); // 预备调用Python模块中的特定函数
PyEval_CallObject(PyObject * function, PyObject * parameterTurple); // 向要调用的Python函数传递参数元组,正式调用该函数
PyObject_CallNoArgs(PyObject * function); // 调用没有参数的函数
PyArg_ParseTuple(PyObject * functionReturn, const char * mode, ...); // 将Python函数传回的值传入C语言变量

从上面的函数列表中,我们看到一个不熟悉的变量格式:PyObject *,这是Python库中的专有格式,专门存储传给Python和Python返回内容的变量。所有传递给Python的值必须是这个类型。
接下来是一个完整的调用示例,通过这个示例来认识调用结构。
PYTHON 共 5 行
展开
1
2
3
4
5
# example.py
def hello(a):
b = "hello"
print(a)
return b,a

C++ 共 27 行
展开
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// main.cpp
#include <iostream>
#include <Python.h>

using namespace std;

int main(int argc, char* argv[])
{
/*构建Python类型的变量*/
PyObject *pyModule, *pyFunction, *pyValue, *pyReturn;
char *a,*b;
Py_Initialize(); // 初始化Python引用
/*必须调用*/
PyRun_SimpleString("import sys"); // 导入模块sys
PyRun_SimpleString("sys.path.append('./')"); // 将python的目录设置到程序根目录
/*构建传入的变量*/
pyValue = Py_BuildValue("s","python"); // 构建变量,第一个参数s代表str,第二个是传入的参数的值
/*调用Python函数*/
pyModule = PyImport_ImportModule("example"); // 导入要使用的模块
pyFunction = PyObject_GetAttrString(pyModule,"hello"); // 定义该模块内要使用的函数名称
pyReturn = pyEval_CallObject(pyFunction,pyValue); // 给模块函数传值,调用函数。
/*从Python取值*/
PyArg_ParseTuple(pyReturn,"ss",&b,&a); // 解析python变量获得返回值
cout << b << " " << a << endl;
Py_Finalize(); // 释放python内存空间
return 0;
}

通过上面的示例,可以大致了解到python的调用方法,虽然不是很简单,但是也不会非常难以理解。更多的用法文档里都有详细的描述,这里就不再重复了。