Windows 下用 C++ 调用 Python

news2024/11/28 12:55:03

文章目录

  • 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+b

def 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_Parse
    PyObject *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_Parse
    PyObject *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 function
    int result = callPythonAdd(helloModule, 1, 3);
    cout << "1 + 3 = " << result << endl;

    // call python get_name function
    std::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.coloradmin.cn/o/1356830.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

从零开始使用Konva,画图并绑定节点。

实战可行&#xff0c;vue3vitets实现 实现电子地图&#xff0c;左侧列表可拖拽绑定 地图可绑定点设备坐标 安装 npm install konva 插件引入 import Konva from konva import Konva from konva import { getImgUrl } from /utils export class konvaManager {public stage…

视频怎么配上音乐?视频软件轻松配乐

视频怎么配上音乐&#xff1f;视频配乐已经成为了一种重要的表达方式。它能够为视频增添情感&#xff0c;营造氛围&#xff0c;让观众更加深入地理解视频的内容。那么&#xff0c;哪些软件可以给视频配上音乐呢&#xff1f;本文将为你介绍几款优秀软件。 一、清爽视频编辑 清爽…

福利来袭,.NET Core开发5大案例,30w字PDF文档大放送!!!

千里之行&#xff0c;始于足下&#xff0c;若想提高软件编程能力&#xff0c;最最重要的是实践&#xff0c;所谓纸上得来终觉浅&#xff0c;绝知此事要躬行。根据相关【艾宾浩斯遗忘曲线】研究表明&#xff0c;如果不动手实践&#xff0c;记住的东西会很快忘记。 为了便于大家查…

虚幻UE 增强输入-第三人称模板增强输入分析与扩展

本篇是增强输入模块&#xff0c;作为UE5.0新增加的模块。 其展现出来的功能异常地强大&#xff01; 让我们先来学习学习一下第三人称模板里面的增强输入吧&#xff01; 文章目录 前言一、增强输入四大概念二、使用步骤1、打开增强输入模块2、添加IA输入动作2、添加IMC输入映射内…

SAFe大规模敏捷企业级实训

课程简介 SAFe – Scaled Agile Framework是目前全球运用最广泛的大规模敏捷框架&#xff0c;也是成长最快、最被认可、最有价值的规模化敏捷框架&#xff0c;目前全球SAFe认证专业人士已达80万人&#xff0c;福布斯100强的70%都在实施SAFe。本课程是一个2天的 SAFe权威培训课…

线程的深入学习(二)

前言 上一篇讲了线程池的相关知识&#xff0c;这篇文章主要讲解一个 1.并发工具类如CountDownLatch、CyclicBarrier等。 2.线程安全和并发集合&#xff1a; 3.学习如何使用Java提供的线程安全的集合类&#xff0c;如ConcurrentHashMap、CopyOnWriteArrayList等。 并发工具类 …

java练习题之String方法运用

应用知识点&#xff1a;​​​​​​String类 1&#xff1a;(String 类)仔细阅读以下代码段&#xff1a; String s "hello"; String t"hello"; char[] c {h,e,l,l,o}; 2&#xff1a;下列选项输出结果为false 的语句是() System.out.println( s.euqals( t…

线段树基础(下)

线段树二分 对序列进行二分的操作&#xff0c;可能使用线段树二分进行优化。 一些序列上最左/最右位置问题可以二分解决&#xff0c;同时需要使用线段树进行查询。时间复杂度通常是 O ( n log ⁡ 2 n ) O(n\log^2n) O(nlog2n)&#xff0c;可以尝试使用线段树二分的技巧将其优…

机器学习(四) -- 模型评估(2)

系列文章目录 机器学习&#xff08;一&#xff09; -- 概述 机器学习&#xff08;二&#xff09; -- 数据预处理&#xff08;1-3&#xff09; 机器学习&#xff08;三&#xff09; -- 特征工程&#xff08;1-2&#xff09; 机器学习&#xff08;四&#xff09; -- 模型评估…

B端产品经理学习-对用户进行需求挖掘

目录&#xff1a; 用户需求挖掘的方法 举例&#xff1a;汽车销售系统的用户访谈-前期准备 用户调研提纲 预约用户做访谈 用户访谈注意点 我们对于干系人做完调研之后需要对用户进行调研&#xff1b;在C端产品常见的用户调研方式外&#xff0c;对B端产品仍然适用的 用户需…

6.1810: Operating System Engineering 2023 <Lab6: Multithreading>

一、本节任务 二、要点 2.1 锁&#xff08;Locking&#xff09; 在多 CPU 或者单 CPU 多线程并发的场景中&#xff0c;对临界资源&#xff08;或者说共享资源&#xff09;的访问如果不加以限制&#xff0c;可能会引发一些严重的问题&#xff0c;比如当两个线程同时对一个共享…

Python初探:从零开始的编程奇妙之旅

一、Python是什么 Python是一门多用途的高级编程语言&#xff0c;以其简洁、易读的语法而脱颖而出。在深度学习领域&#xff0c;Python扮演着至关重要的角色。其丰富的科学计算库&#xff08;如NumPy、Pandas、Matplotlib&#xff09;和强大的深度学习框架&#xff08;如Tenso…

jmeter参数化的三种方式

1.用户定义变量 使用变量&#xff1a; ${变量名} 这个变量是全局变量&#xff0c;也就是在下面子节点中都可以使用&#xff1b; 使用场景&#xff1a;两个账号分别有不同的权限&#xff0c;A经办&#xff0c;B审核。等。。。 2.CSV数据文件设置 3.函数

案例071:基于微信小程序的汽车预约维修系统

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;SSM JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder X 小程序…

jupyter更改默认路径到其它的目录或者到其它的盘 比如D盘

1.打开终端 输入jupyter notebook --generate-config 如下 2.在C:\Users\mb5958\.jupyter路径下 3.用记事本打开它&#xff0c;搜索directory 4.在你想要的路径下新建一个文件夹&#xff0c;如‘D:\jupyterFile’&#xff0c;然后将路径名放在c.NotebookApp.notebook_dir"…

卷麻了,00后测试用例写的比我还好,简直无地自容...........

经常看到无论是刚入职场的新人&#xff0c;还是工作了一段时间的老人&#xff0c;都会对编写测试用例感到困扰&#xff1f;例如&#xff1a; 如何编写测试用例&#xff1f; 作为一个测试新人&#xff0c;刚开始接触测试&#xff0c;对于怎么写测试用例很是头疼&#xff0c;无法…

【PCB专题】Allegro封装更新焊盘

在PCB封装的绘制中&#xff0c;有时会出现需要更新焊盘的情况。比如在制作封装的过程中发现焊盘做的不对而使用PAD_Designer重新更新了焊盘。 那在PCB中如何更新已经修改过的焊盘呢&#xff1f; 打开封装&#xff0c;选择Tools->Padstack->Refresh... 选择Refresh all …

让 sdk 包静默升级的 SAO 操作,你见过几种?

拓展阅读 让 sdk 包静默升级的 SAO 操作&#xff0c;你见过几种&#xff1f; 业务背景 有时候为业务方提供了基础的 sdk 包&#xff0c;为了保证稳定性&#xff0c;一般都是 release 包。 但是每一次升级都非常痛苦&#xff0c;也不可能写一个一步到位的 jar 包&#xff0c…

javascript 常见工具函数(三)

21.克隆数组的几种方法&#xff1a; &#xff08;1&#xff09;slice方法&#xff1a; let arr [1,2,3,4] let arr1 arr.slice() //或者是 let arr1 arr.slice(0) arr[0] 6 console.log(arr) // [6, 2, 3, 4] console.log(arr1) // [1, 2, 3, 4] &#xff08;2&…

Android Jetpack学习系列——Navigation

写在前面 Google在2018年就推出了Jetpack组件库&#xff0c;但是直到今天我才给重视起来&#xff0c;这真的不得不说是一件让人遗憾的事。过去几年的空闲时间里&#xff0c;我一直在尝试做一套自己的组件库&#xff0c;帮助自己快速开发&#xff0c;虽然也听说过Jetpack&#…