您现在的位置是:首页 > 技术教程 正文

Visual Studio 2019下使用C++与Python进行混合编程——环境配置与C++调用Python API接口

admin 阅读: 2024-03-25
后台-插件-广告管理-内容页头部广告(手机)

前言

  1. 在vs2019下使用C++与Python进行混合编程,在根源上讲,Python 本身就是一个C库,那么这里使用其中最简单的一种方法是把Python的C API来嵌入C++项目中,来实现混合编程。
  2. 当前的环境是,win10,IDE是vs2019,python版本是3.9,python的环境是使用Anacond安装的。

一、环境配置

1. 安装Python
首先要安装好Python的库,Python可以直接从官网下载,或者直接在conda里面进行安装。

2.添加环境变量
安装完成之后,添加两个系统环境变量,分别是:PYTHONHOME和PYTHONPATH。
在这里插入图片描述
如果不添加这两个系统环境变量会报以下的错误:

Python path configuration: PYTHONHOME = (not set) PYTHONPATH = (not set) program name = 'python' isolated = 0 environment = 1 user site = 1 import site = 1 sys._base_executable = 'C:\\code\\cpp\\PDFToDoc\\x64\\Release\\PDFToDoc.exe' sys.base_prefix = 'C:\\Users\\duole\\anaconda3' sys.base_exec_prefix = 'C:\\Users\\duole\\anaconda3' sys.platlibdir = 'lib' sys.executable = 'C:\\code\\cpp\\PDFToDoc\\x64\\Release\\PDFToDoc.exe' sys.prefix = 'C:\\Users\\duole\\anaconda3' sys.exec_prefix = 'C:\\Users\\duole\\anaconda3' sys.path = [ 'C:\\Users\\duole\\anaconda3\\python39.zip', '.\\DLLs', '.\\lib', 'C:\\code\\cpp\\PDFToDoc\\x64\\Release', ] Fatal Python error: init_fs_encoding: failed to get the Python codec of the filesystem encoding Python runtime state: core initialized ModuleNotFoundError: No module named 'encodings' Current thread 0x000042d4 (most recent call first): <no Python frame>
  • 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

3. 创建项目
打开vs2019,创建一个空的新C++项目:
在这里插入图片描述
创建完成后打开项目属于配置包含目录与库目录:
在这里插入图片描述
在附加依赖项目里把python的lib库名添加到里面:
在这里插入图片描述
4.添加代码
在项目里面新添一个main.cpp
在这里插入图片描述
main.cpp里面的代码:

#include int main() { Py_Initialize(); // 初始化python解释器 PyRun_SimpleString("print('hello python')"); Py_Finalize(); // 释放资源 return 0; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

然后运行项目
在这里插入图片描述
这样配置就算法成功了。

二、Python C API 调用

为了方便项目测试,在项目根目录下添加一个script目录,在script目录里面创建一个call_python.py的文件。
在这里插入图片描述

2.1 调用Python代码无参函数

C++调用python无参函数流程:

  1. 初始化python接口(Py_Initialize)
  2. 导入依赖库 (PyRun_SimpleString)
  3. 初始化python系统文件路径(PyRun_SimpleString)
  4. 调用python文件名(PyImport_ImportModule)
  5. 获取函数对象(PyObject_GetAttrString)
  6. 调用函数对象(PyObject_CallObject)
  7. 结束python接口调用,释放资源(Py_Finalize)

在call_python.py里面添加代码:

def test(): print("hello python to C++")
  • 1
  • 2

然后在main.cpp里面进行调用:

int main() { //1.初始化python接口 Py_Initialize(); if (!Py_IsInitialized) { std::cout << "python init failed" << std::endl; return 1; } //2.导入依赖库 PyRun_SimpleString("import sys");//执行py单条语句 //3.初始化python系统文件路径,以便访问到python源码文件所在的路径 PyRun_SimpleString("sys.path.append('./script')"); //4.调用python源码文件,只写文件名,不用写后缀 PyObject* module = PyImport_ImportModule("call_python"); if (module == nullptr) { std::cout << "module not found: call_python" << std::endl; return 1; } //5.获取python文件里面的函数 PyObject* test = PyObject_GetAttrString(module, "test"); if (!test || !PyCallable_Check(test)) { std::cout << "function not found: test" << std::endl; return 1; } //6.调用函数,函数对象与传入参数 PyObject_CallObject(test, nullptr); Py_Finalize(); return 0; }
  • 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
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

2.2 调用Python代码有参与有返回值的函数

C++调用python有参并有返回的函数流程:

  1. 初始化python接口(Py_Initialize)
  2. 导入依赖库 (PyRun_SimpleString)
  3. 初始化python系统文件路径(PyRun_SimpleString)
  4. 调用python文件名(PyImport_ImportModule)
  5. 获取函数对象(PyObject_GetAttrString)
  6. 传递参数(PyTuple_New,Py_BuildValue)
  7. 调用函数对象(PyObject_CallObject)
  8. 接收函数返回值(PyArg_Parse)
  9. 结束python接口初始化(Py_Finalize)

在call_python.py里面添加代码:

def add(a, b): c = a + b print(f"{a} + {b} = {c}") return c
  • 1
  • 2
  • 3
  • 4

然后在main.cpp里面进行调用:

#include #include int main() { // 1、初始化python接口 Py_Initialize(); if (!Py_IsInitialized()) { std::cout << "python init failed" << std::endl; return 1; } // 2、初始化python系统文件路径,保证可以访问到 .py文件 PyRun_SimpleString("import sys"); PyRun_SimpleString("sys.path.append('./script')"); // 3、调用python文件名,不用写后缀 PyObject* module = PyImport_ImportModule("call_python"); if (module == nullptr) { std::cout << "module not found: call_python" << std::endl; return 1; } // 4、调用函数 PyObject* func = PyObject_GetAttrString(module, "add"); if (!func || !PyCallable_Check(func)) { std::cout << "function not found: add" << std::endl; return 1; } // 5、给 python 传递参数 // 函数调用的参数传递均是以元组的形式打包的, 2表示参数个数 // 如果函数中只有一个参数时,写1就可以了 PyObject* args = PyTuple_New(2); // 0:第一个参数,传入 int 类型的值 1 PyTuple_SetItem(args, 0, Py_BuildValue("i", 1)); // 1:第二个参数,传入 int 类型的值 2 PyTuple_SetItem(args, 1, Py_BuildValue("i", 2)); // 6、使用C++的python接口调用该函数 PyObject* ret = PyObject_CallObject(func, args); // 7、接收python计算好的返回值 int result; // i表示转换成int型变量。 // 在这里,最需要注意的是:PyArg_Parse的最后一个参数,必须加上“&”符号 PyArg_Parse(ret, "i", &result); std::cout << "return is " << result << std::endl; // 8、结束python接口初始化 Py_Finalize(); return 0; }
  • 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
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

Py_BuildValue()函数的作用和PyArg_ParseTuple()的作用相反,它是将C类型的数据结构转换成Python对象,该函数的原型:
PyObject *Py_BuildValue(char *format, …)
该函数可以和PyArg_ParseTuple()函数一样识别一系列的格式串,但是输入参数只能是值,而不能是指针。它返回一个Python对象。
和PyArg_ParseTuple()不同的一点是PyArg_ParseTuple()函数它的第一个参数为元组,Py_BuildValue()则不一定会生成一个元组。它生成一个元组仅仅当格式串包含两个或者多个格式单元,如果格式串为空,返回NONE。
在下面的描述中,括号中的项是格式单元返回的Python对象类型,方括号中的项为传递的C的值的类型。
“s” (string) [char *] :将C字符串转换成Python对象,如果C字符串为空,返回NONE。
“s#” (string) [char *, int] :将C字符串和它的长度转换成Python对象,如果C字符串为空指针,长度忽略,返回NONE。
“z” (string or None) [char *] :作用同"s"。
“z#” (string or None) [char *, int] :作用同"s#“。
“i” (integer) [int] :将一个C类型的int转换成Python int对象。
“b” (integer) [char] :作用同"i”。
“h” (integer) [short int] :作用同"i"。
“l” (integer) [long int] :将C类型的long转换成Pyhon中的int对象。
“c” (string of length 1) [char] :将C类型的char转换成长度为1的Python字符串对象。
“d” (float) [double] :将C类型的double转换成python中的浮点型对象。
“f” (float) [float] :作用同"d"。
“O&” (object) [converter, anything] :将任何数据类型通过转换函数转换成Python对象,这些数据作为转换函数的参数被调用并且返回一个新的Python对象,如果发生错误返回NULL。
“(items)” (tuple) [matching-items] :将一系列的C值转换成Python元组。
“[items]” (list) [matching-items] :将一系列的C值转换成Python列表。
“{items}” (dictionary) [matching-items] :将一系类的C值转换成Python的字典,每一对连续的C值将转换成一个键值对。

2.3 调用Python类

C++调用python类流程:

  1. 初始化python接口(Py_Initialize)
  2. 初始化python系统文件路径(PyRun_SimpleString)
  3. 调用python文件名(PyImport_ImportModule)
  4. 获取类(PyObject_GetAttrString)
  5. 根据类构造函数实例化对象(PyEval_CallObject)
  6. 获取实例的函数对象(PyObject_GetAttrString)
  7. 传递参数(PyTuple_New,Py_BuildValue)
  8. 调用函数对象(PyObject_CallObject)
  9. 接收函数返回值(PyArg_Parse)
  10. 结束python接口初始化(Py_Finalize)

在call_python.py里面添加代码:

class Person: def __init__(self, name, age): self.name = name self.age = age def foo(self): print(f"my name is {self.name}, my age is {self.age}")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

然后在main.cpp里面进行调用:

#include #include int main() { // 1、初始化python接口 Py_Initialize(); if (!Py_IsInitialized()) { std::cout << "python init failed" << std::endl; return 1; } // 2、初始化python系统文件路径,保证可以访问到 .py文件 PyRun_SimpleString("import sys"); PyRun_SimpleString("sys.path.append('./script')"); // 3、调用python文件名,不用写后缀 PyObject* module = PyImport_ImportModule("call_python"); if (module == nullptr) { std::cout << "module not found: call_python" << std::endl; return 1; } // 4、获取类 PyObject* cls = PyObject_GetAttrString(module, "Person"); if (!cls) { std::cout << "class not found: Person" << std::endl; return 1; } // 5、给类构造函数传递参数 // 函数调用的参数传递均是以元组的形式打包的, 2表示参数个数 // 如果函数中只有一个参数时,写1就可以了 PyObject* args = PyTuple_New(2); // 0:第一个参数,传入 int 类型的值 1 PyTuple_SetItem(args, 0, Py_BuildValue("s", "jack")); // 1:第二个参数,传入 int 类型的值 2 PyTuple_SetItem(args, 1, Py_BuildValue("i", 18)); // 6、根据类名实例化对象 PyObject* obj = PyObject_CallObject(cls, args); // 7、根据对象得到成员函数 PyObject* func = PyObject_GetAttrString(obj, "foo"); if (!func || !PyCallable_Check(func)) { std::cout << "function not found: foo" << std::endl; return 1; } // 8、使用C++的python接口调用该函数 PyObject_CallObject(func, nullptr); // 9、结束python接口初始化 Py_Finalize(); return 0; }
  • 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
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
标签:
声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

在线投稿:投稿 站长QQ:1888636

后台-插件-广告管理-内容页尾部广告(手机)
关注我们

扫一扫关注我们,了解最新精彩内容

搜索