使用Python调用C++库见:我的另一篇博客
工程配置
本文使用的项目构建工具为CMake,使用FindPython
工具在CMake工程中找到Python库,注意CMake最低版本为3.12,参考:https://cmake.org/cmake/help/latest/module/FindPython.html
创建call_python.cpp
文件,程序内容在下节详细说明。CMakeLists.txt文件如下:
cmake_minimum_required(VERSION 3.12)
project(CallPython)
find_package (Python COMPONENTS Interpreter Development)
message(STATUS "Python_VERSION: ${Python_INCLUDE_DIRS}")
include_directories(
${PROJECT_SOURCE_DIR}/include
${Python_INCLUDE_DIRS}
)
# 生成目标文件
add_executable(call_python src/call_python.cpp)
# 链接库
target_link_libraries(call_python ${Python_LIBRARIES})
调用方法
使用C++调用Python脚本中的模块,一般需要经过如下几个步骤:
-
使用
Py_Initialize()
函数初始化Python解释器环境 -
添加.py脚本的路径,使用如下函数完成:
// 添加.py的路径 PyRun_SimpleString("import sys"); PyRun_SimpleString("sys.path.append('脚本路径')");
PyRun_SimpleString()
函数也可以用来执行单条Python命令。 -
导入模块,使用
PyImport_ImportModule('模块名')
完成:PyObject* pModule = PyImport_ImportModule("模块名");
-
导入函数,使用
PyObject_GetAttrString(模块指针, "函数名")
来完成:PyObject* pFunc = PyObject_GetAttrString(pModule, "函数名");
-
构造函数的传入参数,要求以元组的形式传入,有两种方式进行构造,一种是使用
Py_BuildValue()
函数直接构造元组,使用方法如下:PyObject* args = Py_BuildValue("");
其中
()
内可以指定参数格式,如下表所示:格式 实际输入 Py_BuildValue("") None Py_BuildValue(“i”, 123) 123 Py_BuildValue(“iii”, 123, 456, 789) 123, 456, 789 Py_BuildValue(“f”, 123.456) 123.456 Py_BuildValue(“fff”, 123.1, 456.2, 789.3) (123.1, 456.2, 789.3) Py_BuildValue(“s”, “hello”) ‘hello’ Py_BuildValue(“(s)”, “hello”) (‘hello’,) Py_BuildValue(“ss”, “hello”, “world”) (‘hello’, ‘world’) Py_BuildValue("(i)", 123) (123,) Py_BuildValue("((ii)(ii)) (ii)", 1, 2, 3, 4, 5, 6) (((1, 2), (3, 4)), (5, 6)) Py_BuildValue("{s:i,s:i}", “abc”, 123, “def”, 456) {‘abc’: 123, ‘def’: 456} 注:实际输入中带
()
的格式才是元组形式,其余均不能直接作为参数传入到函数中。另一种方式是使用
PyTuple_New(<length>)
和PyTuple_SetItem(参数指针, index, value)
函数间接构造元组:PyObject* args = PyTuple_New(2); PyTuple_SetItem(args, 0, Py_BuildValue("s", "python")); PyTuple_SetItem(args, 1, Py_BuildValue("i", 123);
其中
Py_BuildValue()
使用方法见上表。如果传入参数是列表形式,则使用
PyList_New()
来构造一个列表,再将该列表加入到元组中即可// 构造一个列表,并添加成员 PyObject* list = PyList_New(3); PyList_SetItem(list, 0, Py_BuildValue("i", 1)); PyList_SetItem(list, 1, Py_BuildValue("i", 10)); PyList_SetItem(list, 2, Py_BuildValue("i", 100)); // 构造一个元组,将list加入到参数列表 PyObject* args = PyTuple_New(1); PyTuple_SetItem(args, 0, list);
-
执行函数,使用
PyObject_CallObject(函数指针, 参数指针)
完成:PyObject_CallObject(pFunc, args);
如果有返回值,则使用如下程序获取返回值:
PyObject* pRet = PyObject_CallObject(pFunc, args); if (pRet) { long result = PyLong_AsLong(pRet); // 将返回值转换成long型 std::cout << "result:" << result << std::endl ; }
-
结束Python解释器:
Py_Finalize();
举例
创建一个.py文件,命名为calculator.py
,内容如下:
def add(a, b):
print(a + b)
return a + b
def helloworld(s):
print("hello " + s)
现在我们要调用该脚本中的add
函数和helloworld
函数,C++程序如下:
#include <iostream>
#include <Python.h>
int main(int argc, char** argv){
// 运行Python解释器
Py_Initialize();
// 添加.py的路径
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append('<scripts_path>')");
/**************************
********* add函数 **********
**************************/
// 导入模块
PyObject* pModule = PyImport_ImportModule("calculator");
// 导入要运行的函数
PyObject* pFunc = PyObject_GetAttrString(pModule, "add");
// 构造传入参数
PyObject* args = PyTuple_New(2);
PyTuple_SetItem(args, 0, Py_BuildValue("i", 1));
PyTuple_SetItem(args, 1, Py_BuildValue("i", 10));
// 运行函数,并获取返回值
PyObject* pRet = PyObject_CallObject(pFunc, args);
if (pRet)
{
long result = PyLong_AsLong(pRet); // 将返回值转换成long型
std::cout << "result:" << result << std::endl ;
}
/**************************
****** helloworld函数 *****
**************************/
// 导入函数
pFunc = PyObject_GetAttrString(pModule, "helloworld");
// 构造传入参数
PyObject* str = Py_BuildValue("(s)", "python");
// 执行函数
PyObject_CallObject(pFunc, str);
// 终止Python解释器
Py_Finalize();
}
运行结果:
11
result:11
hello python