当前位置: 首页 > news >正文

Windows 下用 C++ 调用 Python

文章目录

  • Part.I Introduction
    • Chap.I Information
    • Chap.II 预备知识
  • Part.II 语法
    • Chap.I PyRun_SimpleString
    • Chap.II C++ / Python 变量之间的相互转换
  • Part.III 实例
    • Chap.I 文件内容
    • Chap.II 基于 Visual Studio IDE
    • Chap.III 基于 cmake
    • Chap.IV 运行结果
  • Part.IV 可能出现的问题
    • Chap.I 无法打开 python311_d.lib
    • Chap.II 导入模块报错
    • Chap.I PyEval_CallObject 调用报错
  • Reference

Part.I Introduction

在这里插入图片描述

本文主要介绍一下如何使用 C++ 调用 Python。包括运行 python 脚本;C++ 和 Python 之间参数的相互转换和传递。

Chap.I Information

下面是笔者python 相关的目录,可参考

  • python.exe 所在目录:A:\Programs\Python\Python11_4
  • python311.lib 所在目录:A:\Programs\Python\Python11_4\libs
  • include 所在目录:A:\Programs\Python\Python11_4\include

Chap.II 预备知识

Part.II 语法

Chap.I PyRun_SimpleString

Python 库函数 PyRun_SimpleString 可以执行字符串形式的Python代码。不过在使用 PyRun_SimpleString 之前需要先初始化(Py_Initialize()),执行完之后需要释放资源Py_Finalize(),示例:

Py_Initialize();
PyRun_SimpleString("print('Hello!')");
Py_Finalize();

Chap.II C++ / Python 变量之间的相互转换

有待总结和补充,可看下面的实例。

Part.III 实例

Chap.I 文件内容

所用到的几个文件组织结构如下

test
│  CMakeLists.txt
│  hello.py
│  main.cpp
└─build

hello.py中有两个小函数,内容如下:

def add(a,b):return a+bdef get_name(first):return "your name is {} alice".format(first)

main.cpp文件内容如下:

#include <iostream>
#include <Python.h>using namespace std;const int kError = -1;
const int kSuccess = 0;/*** @brief  Initializes the python interpreter*/
int pythonInit() {Py_Initialize();int ret = Py_IsInitialized();if (ret == 0) {cout << "Py_Initialize error" << endl;return kError;}return kSuccess;
}/*** @brief  Release resources requested by Python*/
void pythonCleanup() {Py_Finalize();
}/*** @brief  Import module ${name} in ${pyDir}* @param[in] pyDir     The path of the python script* @param[in] name      The name of the python script* @return              the module object*/
PyObject *pythonImportModule(const char *pyDir, const char *name) {// 引入当前路径,否则下面模块不能正常导入char tempPath[256] = {};sprintf(tempPath, "sys.path.append('%s')", pyDir);PyRun_SimpleString("import sys");//PyRun_SimpleString("sys.path.append('./')");PyRun_SimpleString(tempPath);PyRun_SimpleString("print('curr sys.path', sys.path)");// import ${name}PyObject *module = PyImport_ImportModule(name);if (module == nullptr) {PyErr_Print();cout << "PyImport_ImportModule '" << name << "' not found" << endl;return nullptr;}return module;
}/*** @brief  Call 'add' function in the python script* @param[in] module    The name of module* @param[in] a         The value of a* @param[in] b         The value of b* @return              a + b*/
int callPythonAdd(PyObject *module, int a, int b) {//获取模块字典属性PyObject *pDict = PyModule_GetDict(module);if (pDict == nullptr) {PyErr_Print();std::cout << "PyModule_GetDict error" << std::endl;return kError;}//直接获取模块中的函数PyObject *addFunc = PyDict_GetItemString(pDict, "add");if (addFunc == nullptr) {std::cout << "PyDict_GetItemString 'add' not found" << std::endl;return kError;}// 构造python 函数入参, 接收2// see: https://docs.python.org/zh-cn/3.7/c-api/arg.html?highlight=pyarg_parse#c.PyArg_ParsePyObject *pArg = Py_BuildValue("(i,i)", a, b);//调用函数,并得到 python 类型的返回值PyObject *result = PyEval_CallObject(addFunc, pArg);int ret = 0;//将python类型的返回值转换为c/c++类型PyArg_Parse(result, "i", &ret);return ret;
}/*** @brief  Call 'get_name' function in the python script* @param[in] module    The name of module* @param[in] firstName The firstname* @param[in] outName   The fullname* @return              success or not*/
int callPythonGetName(PyObject *module, std::string firstName, std::string &outName) {//获取模块字典属性PyObject *pDict = PyModule_GetDict(module);if (pDict == nullptr) {PyErr_Print();std::cout << "PyModule_GetDict error" << std::endl;return kError;}//直接获取模块中的函数PyObject *addFunc = PyDict_GetItemString(pDict, "get_name");if (addFunc == nullptr) {std::cout << "PyDict_GetItemString 'add' not found" << std::endl;return kError;}// 构造python 函数入参, 接收2// see: https://docs.python.org/zh-cn/3.7/c-api/arg.html?highlight=pyarg_parse#c.PyArg_ParsePyObject *pArg = Py_BuildValue("(s)", firstName.c_str());//调用函数,并得到python类型的返回值PyObject *result = PyEval_CallObject(addFunc, pArg);char *name = nullptr;//将python类型的返回值转换为c/c++类型PyArg_Parse(result, "s", &name);char tempStr[256] = {};int strLen = strlen(name);if (strLen > 256) {return kError;}strcpy(tempStr, name);outName = tempStr;return kSuccess;
}int main() {pythonInit();//直接运行python代码PyRun_SimpleString("print('---------- Hello Python form C/C++ ----------')");PyObject *helloModule = pythonImportModule("../", "hello");    // 这里最好还是给绝对路径吧if (helloModule == nullptr) {return -1;}// call python add functionint result = callPythonAdd(helloModule, 1, 3);cout << "1 + 3 = " << result << endl;// call python get_name functionstd::string fullName;callPythonGetName(helloModule, "summer", fullName);cout << fullName << endl;pythonCleanup();
}

CMakeLists.txt 文件内容等会儿说;build 是一个空文件,生成的二进制文件等放这里面。

Chap.II 基于 Visual Studio IDE

1、首先新建一个工程,将main.cpp添加进去,然后将hello.py放在和main.cpp一样的路径下。

2、将 IDE 上方『解决方案平台』设置为x64,最好将『解决方案配置』设置为Release(debug 需要*_d.lib

3、将include添加到 C/C++ 附加包含目录中:项目右键→属性→C/C++→附加包含目录→添加 python 的 include

在这里插入图片描述
右键属性→链接器→常规→附加库目录→将 python 的 libs 加进去

在这里插入图片描述
3、将*.lib添加到附加依赖项中:项目右键→属性→链接器→输入→将python311.libpython311_d.lib加进去。

在这里插入图片描述

Chap.III 基于 cmake

cmake 自动搜寻 python

CMakeLists.txt文件内容如下:

cmake_minimum_required( VERSION 3.20 )project( test )set( PRJ_INCLUDE_DIRS )
set( PRJ_COMPILE_FEATURES )
set( PRJ_LIBRARIES )list( APPEND PRJ_COMPILE_FEATURES cxx_std_20 )find_package(Python3 COMPONENTS Interpreter Development)message( STATUS "Python3_FOUND = ${Python3_FOUND} " )
message( STATUS "Python3_INCLUDE_DIRS = ${Python3_INCLUDE_DIRS} " )
message( STATUS "Python3_LIBRARIES = ${Python3_LIBRARIES} " )if( ${Python3_FOUND} )#include_directories(${Python3_INCLUDE_DIRS})
else()message( FATAL_ERROR "Python3 not found, please install it." )
endif()list( APPEND PRJ_INCLUDE_DIRS ${Python3_INCLUDE_DIRS} )
list( APPEND PRJ_LIBRARIES ${Python3_LIBRARIES} )message( STATUS "PRJ_LIBRARIES = ${PRJ_LIBRARIES} " )add_executable( ${PROJECT_NAME}main.cpp
)target_include_directories( ${PROJECT_NAME}PRIVATE ${PRJ_INCLUDE_DIRS}
)target_link_libraries( ${PROJECT_NAME} PRIVATE ${PRJ_LIBRARIES}
)target_compile_features( ${PROJECT_NAME} PRIVATE ${PRJ_COMPILE_FEATURES}
)

用 CMake 编译一下
在这里插入图片描述
然后直接跑就可以!


cmake 手动设置 Python 路径

CMakeLists.txt文件内容如下:

cmake_minimum_required( VERSION 3.20 )project( test )set( PRJ_INCLUDE_DIRS )
set( PRJ_COMPILE_FEATURES )
set( PRJ_LIBRARIES )list( APPEND PRJ_COMPILE_FEATURES cxx_std_20 )set( Python3_INCLUDE_DIRS "A:/Programs/Python/Python11_4/include")
set( Python3_LIBRARIES "A:/Programs/Python/Python11_4/libs/python311.lib""A:/Programs/Python/Python11_4/libs/python311_d.lib" )message( STATUS "Python3_INCLUDE_DIRS = ${Python3_INCLUDE_DIRS} " )
message( STATUS "Python3_LIBRARIES = ${Python3_LIBRARIES} " )list( APPEND PRJ_INCLUDE_DIRS ${Python3_INCLUDE_DIRS} )
list( APPEND PRJ_LIBRARIES ${Python3_LIBRARIES} )message( STATUS "PRJ_LIBRARIES = ${PRJ_LIBRARIES} " )add_executable( ${PROJECT_NAME}main.cpp
)target_include_directories( ${PROJECT_NAME}PRIVATE ${PRJ_INCLUDE_DIRS}
)target_link_libraries( ${PROJECT_NAME} PRIVATE ${PRJ_LIBRARIES}
)target_compile_features( ${PROJECT_NAME} PRIVATE ${PRJ_COMPILE_FEATURES}
)

Chap.IV 运行结果

---------- Hello Python form C/C++ ----------
curr sys.path ['A:\\Programs\\Python\\Python11_4\\python311.zip', 'A:\\Programs\\Python\\Python11_4\\DLLs', 'A:\\Programs\\Python\\Python11_4\\Lib', 'A:\\aWork\\scripts\\test1\\test\\build\\Debug', 'A:\\Programs\\Python\\Python11_4', 'A:\\Programs\\Python\\Python11_4\\Lib\\site-packages', '../']
1 + 3 = 4
your name is summer alice

Part.IV 可能出现的问题

Chap.I 无法打开 python311_d.lib

无法打开 python311_d.lib 的问题:笔者使用的 python 版本是 Python 11.4,它的libs中没有python311_d.lib,只有python311.lib(因为安装的时候没有勾选安装 debug 的 lib),解决方法有三个:

  1. 不使用debug模式运行程序,使用release或其他模式运行 C++ 程序
  2. 找到pyconfig.h文件(一般在py_dir/include文件夹下),注释下面的内容(有点危险)
#ifdef _DEBUG
#       define Py_DEBUG
#endif
  1. 安装 python 的 debug 版本库:安装程序→更改→勾选Download debug binaries (requires VS 2017 or later)

Chap.II 导入模块报错

这种情况下是没有把 python 脚本所在的路径加到sys.path里面,使用sys.path.append(your_path_xx)添加一下就可以了。

Chap.I PyEval_CallObject 调用报错

报错内容:

'PyEval_CallObjectWithKeywords': deprecated in 3.9

PyEval_CallObject替换为PyObject_CallObject就行了。

Reference

  • Linux 系统下通过 cmake 使 C++ 调用 Python
http://www.lryc.cn/news/273694.html

相关文章:

  • 九州金榜|家庭教育一招孩子不在任性
  • 爬虫案列 --抖音视频批量爬取
  • 【React系列】React中的CSS
  • 基于Kettle开发的web版数据集成开源工具(data-integration)-应用篇
  • 51单片机三种编译模式的相互关系
  • java 千帆大模型 流式返回
  • 全新互联网洗衣洗鞋小程序平台新模式
  • js 对于一些脚本中对于url的一些参数获取
  • IEDA中tomcat日志乱码解决
  • 计算机网络实验(六):三层交换机实现VLAN间路由
  • Flutter中showModalBottomSheet的属性介绍和使用
  • 机器学习 -- k近邻算法
  • 安全测试之SSRF请求伪造
  • php composer安装
  • 【MyBatis】MyBatis基础操作
  • Automatic merge failed; fix conflicts and then commit the result.如何处理
  • 一文读懂 $mash 通证 “Fair Launch” 规则(幸运池玩法解读篇)
  • Qt3D QGeometryRenderer几何体渲染类使用说明
  • pandasDataFrame读和写csv文件
  • 力扣122. 买卖股票的最佳时机 II
  • Go语言断言和类型查询
  • 02 Deep learning algorithm
  • 代码随想录算法训练营第二十四天 | 回溯算法
  • Spring Cloud Gateway 缓存区异常
  • Spring Boot依赖版本声明
  • Java项目:109SpringBoot超市仓管系统
  • 【React系列】Redux(三) state如何管理
  • 3D 纹理的综合指南
  • LLM之RAG实战(十一)| 使用Mistral-7B和Langchain搭建基于PDF文件的聊天机器人
  • VLOOKUP的使用方法