C/C++调用python

编程入门 行业动态 更新时间:2024-10-28 02:28:56

C/C++调用<a href=https://www.elefans.com/category/jswz/34/1770869.html style=python"/>

C/C++调用python

python环境

通过如下的命令,可以获取到当前python环境下对应的路径。比如程序编译需要指定对应的头文件、库文件路径,链接阶段需要指定需要链接哪些库。

# 说明python3-config支持哪些选项
$ python3-config 
Usage: /home/yangye/miniconda3/bin/python3-config --prefix|--exec-prefix|--includes|--libs|--cflags|--ldflags|--extension-suffix|--help|--abiflags|--configdir|--embed$ python3-config --prefix
/home/yangye/miniconda3
# 对应的头文件路径
$ python3-config --includes
-I/home/yangye/miniconda3/include/python3.10 -I/home/yangye/miniconda3/include/python3.10
# 对于python3.8 以下通过--libs可以获取对应的链接库名称
$ python3-config --libs-lcrypt -lpthread -ldl  -lutil -lm -lm 
# python3.8以上需要加上--embed
$ python3-config --libs --embed
-lpython3.10 -lcrypt -lpthread -ldl  -lutil -lm -lm $ python3-config --cflags
-I/home/yangye/miniconda3/include/python3.10 -I/home/yangye/miniconda3/include/python3.10  -Wno-unused-result -Wsign-compare -march=nocona -mtune=haswell -ftree-vectorize -fPIC -fstack-protector-strong -fno-plt -O3 -ffunction-sections -pipe -isystem /home/yangye/miniconda3/include -fdebug-prefix-map=/croot/python-split_1679423815169/work=/usr/local/src/conda/python-3.10.10 -fdebug-prefix-map=/home/yangye/miniconda3=/usr/local/src/conda-prefix -fuse-linker-plugin -ffat-lto-objects -flto-partition=none -flto -DNDEBUG -fwrapv -O3 -Wall
# 对应的库文件路径
$ python3-config --ldflags
-L/home/yangye/miniconda3/lib/python3.10/config-3.10-x86_64-linux-gnu -L/home/yangye/miniconda3/lib  -lcrypt -lpthread -ldl  -lutil -lm -lm $ python3-config --extension-suffix.cpython-310-x86_64-linux-gnu.so
$ python3-config --abiflags$ python3-config --configdir
/home/yangye/miniconda3/lib/python3.10/config-3.10-x86_64-linux-gnu

编译动态库

python支持将一个模块编译成动态库,然后对动态库进行调用,与import模块的效果一样。下面是一个简单的例子,说明编译动态库的过程。

  1. 安装Cython
 pip install Cython
  1. 编写python模块
def hello(s):print(s)
  1. 编写setup.py文件
from distutils.core import setup
from Cython.Build import cythonizesetup(name = "sharedModule",ext_modules= cythonize("sharedModule.py")
)
  1. 编译
python setup.py build_ext --inplace

C++调用Python

本文重点是介绍如何使用C/C++调用python程序,具体实现代码参考了如下文章。C语言调用Python脚本的原理主要基于Python提供的C API。Python的C API允许C语言程序调用Python解释器,并进行交互操作,如执行Python代码、调用Python函数等。
C++使用Python/C API_c++ 调用api_Eliza_Her的博客-CSDN博客

CMake

下面是一个CMake编译对应的CMakeFiles.txt文件内容,具体可以执行定义的命令替换相应的路径。

cmake_minimum_required(VERSION 3.19)
project(cppCallPythonFunc)#set(CMAKE_CXX_STANDARD 14)
# python3-config --includes
include_directories("/home/xxxx/miniconda3/include/python3.10"
)#在控制台运行python3.6-config --ldflags 取得参数
#-L/usr/lib/python3.6/config-3.6m-x86_64-linux-gnu -L/usr/lib -lpython3.6m -lpthread -ldl  -lutil -lm  -Xlinker -export-dynamic -Wl,-O1 -Wl,-Bsymbolic-functions# 添加动态库的位置
LINK_DIRECTORIES("/home/xxxx/miniconda3/lib/python3.10/config-3.10-x86_64-linux-gnu" "/home/xxxx/miniconda3/lib")
# LINK_DIRECTORIES("/usr/lib")add_executable(main main.cpp)
target_link_libraries(main -lpython3.10 -lcrypt -lpthread -ldl  -lutil -lm )

调用python

C/C++调用Python脚本的基本步骤:

  1. 初始化Python解释器:使用Py_Initialize()函数,这是调用Python脚本之前必须的步骤。
  2. 调用Python代码:可以使用PyRun_SimpleString()函数执行Python代码,或者使用PyImport_ImportModule()函数导入Python模块。
  3. 获取并调用Python函数:首先,可以使用PyObject_GetAttrString()或者PyObject_GetAttr()获取Python对象(例如,模块或类)的属性(例如,函数)。然后,可以使用PyObject_CallObject()PyObject_CallFunction()调用获取到的Python函数。
  4. 处理Python函数的返回值:可以使用Python的C API提供的函数来获取和处理Python函数的返回值。
  5. 释放资源并关闭Python解释器:使用Py_Finalize()函数。

直接运行python代码段

#include <Python.h>
#include <iostream>
using namespace std;int main(int argc, char *argv[]) 
{// 初始化python解释器.C/C++中调用Python之前必须先初始化解释器Py_Initialize();if(Py_IsInitialized()){std::cout << "python translator initialized." << std::endl;}// 执行一个简单的执行python脚本命令PyRun_SimpleString("print('hello world')\n");// 撤销Py_Initialize()和随后使用Python/C API函数进行的所有初始化// 并销毁自上次调用Py_Initialize()以来创建并为被销毁的所有子解释器。Py_Finalize();return 0;
}

直接运行python脚本

#include <Python.h>
#include <iostream>
using namespace std;int main(int argc, char *argv[]) 
{// 初始化python解释器.C/C++中调用Python之前必须先初始化解释器Py_Initialize();if(Py_IsInitialized()){std::cout << "python translator initialized." << std::endl;}// 运行python脚本PyRun_SimpleString("import sys");PyRun_SimpleString("sys.path.append('..')");// 输入python脚本文件名string filename = "../simple_run.py";PyObject* obj = Py_BuildValue("s", filename.c_str());FILE* file = _Py_fopen_obj(obj, "r+");if (file != NULL){PyRun_SimpleFile(file, filename.c_str());}// 撤销Py_Initialize()和随后使用Python/C API函数进行的所有初始化// 并销毁自上次调用Py_Initialize()以来创建并为被销毁的所有子解释器。Py_Finalize();return 0;
}

导入python模块,调用python函数

解析参数并构建值变量
如何将二维数组从C语言传递到Python?
C调用Python(传递数字、字符串、list数组(一维、二维),结构体)_c++与python 传递数组-CSDN博客
.html
以下代码是Py_BuildValue函数构建不同类型的数据,作为python函数的输入,可以做一个参考。

Py_BuildValue("") None
Py_BuildValue("i", 123) 123
Py_BuildValue("iii", 123, 456, 789) (123, 456, 789)
Py_BuildValue("s", "hello") 'hello'
Py_BuildValue("ss", "hello", "world") ('hello', 'world')
Py_BuildValue("s#", "hello", 4) 'hell'
Py_BuildValue("()") ()
Py_BuildValue("(i)", 123) (123,) 
Py_BuildValue("(ii)", 123, 456) (123, 456)
Py_BuildValue("(i,i)", 123, 456) (123, 456)
Py_BuildValue("[i,i]", 123, 456) [123, 456] Py_BuildValue("{s:i,s:i}", "abc", 123, "def", 456) {'abc': 123, 'def': 456}
Py_BuildValue("((ii)(ii)) (ii)", 1, 2, 3, 4, 5, 6) (((1, 2), (3, 4)), (5, 6))

以下是一个简单的python模块,定义了三种函数,无参数,简单类型的参数和list参数输入。

import time
import numpy as np
import torch
import cv2def print_time():print(f"now {time.ctime()}")def add_num(a, b):print(np.__version__)print(torch.__version__)print(cv2.__version__)return a + bdef test_lst(lst):print(lst)

下面是具体的C++代码实现,导入文件的模块,然后调用其中的函数内容。测试list有好多坑呀。

#include <Python.h>
#include <iostream>
using namespace std;int main(int argc, char *argv[]) 
{// 初始化python解释器.C/C++中调用Python之前必须先初始化解释器Py_Initialize();if(Py_IsInitialized()){std::cout << "python translator initialized." << std::endl;}PyRun_SimpleString("import sys");PyRun_SimpleString("sys.path.append('..')");PyObject* pModule = NULL;PyObject* pFunc = NULL;PyObject* args = NULL;//import模块pModule = PyImport_ImportModule("simple_module");//模块文件名//找不到模块则报错if (pModule == nullptr) {PyErr_Print();Py_Finalize();return 0;}//1. 调用不带参数的函数 Hello()pFunc = PyObject_GetAttrString(pModule, "print_time");//函数名PyObject_CallFunction(pFunc, NULL);//调用函数//2. 调用带参数的函数 Add(a, b)pFunc = PyObject_GetAttrString(pModule, "add_num");args = Py_BuildValue("(ii)", 123, 456); //设置传入Add的参数PyObject* pRet = PyObject_CallObject(pFunc, args); //pRet = Add(123, 456)// 3. 解析返回值int ans = 0;PyArg_Parse(pRet, "i", &ans);	//返回类型转换printf("Return C++: ans = %d\n", ans);// 测试一维数组double CArray[] = {1.2, 4.5, 6.7, 8.9, 1.5, 0.5};PyObject *PyList  = PyList_New(6);PyObject *ArgList = PyTuple_New(1);for(int i = 0; i < PyList_Size(PyList); i++)PyList_SetItem(PyList,i, PyFloat_FromDouble(CArray[i]));//给PyList对象的每个元素赋值PyTuple_SetItem(ArgList, 0, PyList);//将PyList对象放入PyTuple对象中pFunc = PyObject_GetAttrString(pModule, "test_lst");//函数名PyObject_CallObject(pFunc, ArgList);//调用函数,完成传递// 撤销Py_Initialize()和随后使用Python/C API函数进行的所有初始化// 并销毁自上次调用Py_Initialize()以来创建并为被销毁的所有子解释器。Py_Finalize();return 0;
}

调用python类

python中类的使用比较普遍,下面重点介绍一下如何导入python类,并访问对应的属性和方法,具体参考了如下文章。
【精选】C调用python类的正确方法-CSDN博客
下面的代码定义了一个类,并导入了深度学习中常用的库作为测试,并测试了list作为输入的情况。

import numpy as np
import torch
import cv2class myClass:welcome = "Hello from Python class attribute"def hello(self):print(np.__version__)print(torch.__version__)print(cv2.__version__)print("Hello from Python class method")def minus(self, a, b):print(str(a) + " - " + str(b) + " = " + str(a-b))return a-bdef print_lst(self, lst):print(lst)

以下是对应的C++代码,具体可以参考注释。

#include <Python.h>
#include <iostream>
using namespace std;int main(int argc, char *argv[]) 
{// 初始化python解释器.C/C++中调用Python之前必须先初始化解释器Py_Initialize();if(Py_IsInitialized()){std::cout << "python translator initialized." << std::endl;}PyRun_SimpleString("import sys");PyRun_SimpleString("sys.path.append('..')");PyObject* pModule = NULL;PyObject* pFunc = NULL;PyObject* args = NULL;//import模块pModule = PyImport_ImportModule("simple_class");//模块文件名//找不到模块则报错if (pModule == nullptr) {PyErr_Print();Py_Finalize();return 0;}// 模块的字典列表PyObject* pDict = PyModule_GetDict(pModule);if (!pDict) {PyErr_Print();Py_Finalize();return 0;}//从字典中获取myClass类PyObject* pClassCalc = PyDict_GetItemString(pDict, "myClass");if (!pClassCalc) {PyErr_Print();Py_Finalize();return 0;}// 获取构造函数PyObject* pConstruct = PyInstanceMethod_New(pClassCalc);if (!pConstruct) {PyErr_Print();Py_Finalize();return 0;}// 调用构造函数 构建对象PyObject* pIns = PyObject_CallObject(pConstruct,nullptr);//获取pIns实例的属性,转换成字符串并输出PyObject* obj2 = PyObject_GetAttrString(pIns, "welcome");PyObject* str = PyUnicode_AsEncodedString(obj2, "utf-8", "strict");char* result = PyBytes_AsString(str);printf("%s\n", result);//如果属性是int型,可用下面这句转换属性://int qwq;//PyArg_Parse(obj2, "i", &qwq);// args = Py_BuildValue("(ii)", 123, 456); //设置传入Add的参数args = PyTuple_New(2);PyTuple_SET_ITEM(args, 0, Py_BuildValue("i", 1));PyTuple_SET_ITEM(args, 1, Py_BuildValue("i", 2));PyObject_CallMethod(pIns, "print_lst", "(O)", args);//调用无参数Python方法PyObject_CallMethod(pIns, "hello", nullptr);//调用多参数Python方法PyObject* pRet = PyObject_CallMethod(pIns, "minus","(i,i)", 12, 22);if (!pRet){PyErr_Print();Py_Finalize();return 0;}int res = 0;PyArg_Parse(pRet, "i", &res);//转换返回类型printf("Return C++: ans = %d\n", res);// 撤销Py_Initialize()和随后使用Python/C API函数进行的所有初始化// 并销毁自上次调用Py_Initialize()以来创建并为被销毁的所有子解释器。Py_Finalize();return 0;
}

从动态库中导入python模块

参考第二小节:编译动态库,得到动态库,将其拷贝到main.cpp的同级目录下,此时可以以动态库的形式加载python模块,后续使用和之前文件一样。
项目的整体文件结构如下:

.
├── [ 761]  CMakeLists.txt
├── [ 13K]  main
├── [2.3K]  main.cpp
├── [ 57K]  simple_class.cpython-310-x86_64-linux-gnu.so
├── [ 102]  simple_module.py
├── [ 113]  simple_run.py
└── [4.0K]  test_shared├── [4.0K]  build│   ├── [4.0K]  lib.linux-x86_64-cpython-310│   │   └── [ 57K]  simple_class.cpython-310-x86_64-linux-gnu.so│   └── [4.0K]  temp.linux-x86_64-cpython-310│       └── [ 56K]  simple_class.o├── [ 150]  setup.py├── [269K]  simple_class.c├── [ 57K]  simple_class.cpython-310-x86_64-linux-gnu.so└── [ 241]  simple_class.py

更多推荐

C/C++调用python

本文发布于:2023-11-15 04:52:30,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1593992.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:python

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!