c++和python的互相调用

文章目录

  • 前提
  • ctypes
  • pybind11
    • 在python中调用C++
    • 在C++中调用python
  • Cython
    • 加快python速度
    • 在C中调用python代码
  • 调用Python的原生C API
  • 参考链接

前提

因项目需求,需要在C++中调用python,对这方面的一些工具做个简单的介绍。

ctypes

ctypes 是 Python 的外部函数库。它提供了与 C 兼容的数据类型,并允许调用 DLL 或共享库中的函数。可使用该模块以纯 Python 形式对这些库进行封装。
上面是ctypes官方文档给出的介绍,通俗理解来说:ctypes可以加载动态链接库,然后以此调用动态链接库中的函数。也就是说,如果我们有一个.c文件,我们可以将它编译成库,然后在python代码里面使用ctypes加载调用它。
相关代码如下:

  1. 创建一个main.c文件,包括三个函数,等会我们要通过调用动态链接库的方式在python中调用这三个函数。
// main.c
#include <stdio.h>
#include <stdlib.h>int add(int a, int b) {return a + b;
}int sum(int *a, int num){int sum = 0;for(int i=0; i<num; i++){sum += a[i];}return sum;
}
  1. main.c编译为动态链接库mainlib.dll
gcc -shared -o mainlib.dll main.c
  1. 现在我们的文件夹下便会多出一个mainlib.dll库文件,接下来我们在python中调用并且使用它。
# demo.py
import ctypes
from ctypes import *
mainlib = ctypes.CDLL('test/mainlib.dll')a = ctypes.c_int(1)
b = ctypes.c_int(2) 
print(mainlib.add(a,b))# 要想传入int类型的数组,就必须按照下面的方式先进行定义
int_array = (c_int * 3)(1, 2, 3)
num = ctypes.c_int(3)
print(mainlib.sum(int_array, 3))

总结: ctypes可以应用到在python中调用c函数,也就是python调用C,也就是扩展python。

pybind11

pybind11之前我使用过,当时的场景是:有一个深度学习算子是用c++和cuda写的,要把它接入到pytorch中,相当于是python中调用c++。当时的解决方案是:使用pybind11这个工具将这个算子封装成动态库文件,然后在python端进行加载运行。

在这里,我可以很明确的告诉大家:pybind11可以使我们在python中调用C++(这是pybind11的主要目的和应用),也可以使我们在C++中调用python。 下面给出两个示例。

在python中调用C++

  1. 安装pybind11
    这里我建议使用conda install 的方式安装pybind11,否在后面在C++中会找不到pybind的头文件等。
conda install pybind11
  1. 创建main.cpp
#include <pybind11/pybind11.h>namespace py = pybind11;int add(int i, int j){return i+j;
}PYBIND11_MODULE(example, m) {m.doc() = "pybind11 example plugin"; // optional module docstringm.def("add", &add, "A function that adds two numbers");
}

可以看到,在main.c中定义了一个add函数。后面几行添加了pybind接口的代码。

  1. 生成动态链接库
    生成动态链接库这一部分,很多教程中使用的都是一个setup.py,这里我使用从官网得到的命令行生成.so文件。
c++ -O3 -Wall -shared -std=c++11 -fPIC $(python3 -m pybind11 --includes) main.cpp -o example$(python3-config --extension-suffix)

运行完这条指令后,可以看到文件夹中多了一个以example开头的.so文件。

  1. 在python中调用该动态链接库
import example
example.add(1,2)  # 3

以上就是使用pybind11在python中调用c++的全流程。

在C++中调用python

这一部分互联网上资源很少,我没有找到一个完整的demo,最后从pybind11的官网 找到了一些demo,这里进行展示。

  1. 准备c++环境
    因为我是使用cmake编译代码,所以第一步要找到pybind11的头文件,也就是确保CmakeLists.txt文件正确。下面是我的cmake文件。
cmake_minimum_required(VERSION 3.16)
project(main)find_package(pybind11 REQUIRED)  # 寻找pybind11add_executable(main main.cpp)
target_link_libraries(main pybind11::embed)
  1. demo
#include <pybind11/embed.h> // 注意,这里的头文件和上一个不一样
#include <iostream>
namespace py = pybind11;int main(){py::scoped_interpreter guard{};  // 激活python解释器py::print("hello, world!");  // 使用python apipy::exec(R"(kwargs = dict(name="World", number=42)message = "Hello, {name}! The answer is {number}".format(**kwargs)print(message))");  // 使用exec在c++中运行python代码// 在c++中导入python的模块py::module_ sys = py::module_::import("sys");py::print(sys.attr("path"));  // 为了简单起见,现在的工作目录已经被添加到了`sys.path`里面。/* 1. 创建calc.py**"""calc.py located in the working directory"""*   def add(i, j):*       return i + j*/// 2. import calc modulepy::module_ calc = py::module_::import("calc");// 3. call calc module's methodpy::object result = calc.attr("add")(1,2);int n = result.cast<int>();std::cout<<"n = "<<n<<std::endl;return 0;
}

Cython

这里先强调一点:Cython和CPython是完全不同的两个东西以及这篇文章。
Cython是一门结合了C和Python的编程语言(Cython是python的超集),接下来我们给出Cython几种不同的作用,但是无论如何,在linux下Cython最后都会生成一个.so文件。

加快python速度

我们有一个python写的斐波那契数列,但是运行速度太慢,因为Cython中有C语言的特性,所以我们可以使用Cython语言重写斐波那契数列,然后编译为动态链接库,然后在python代码中使用。
代码如下:
1.斐波那契数列原始的python代码:

## fib.py
def fib(n):a, b = 0.0, 1.0for i in range(n):a, b = a + b, areturn a

用Cython重写的斐波那契数列,文件后缀名为.pyx:

## fib.pyx
def fib(int n):cdef int icdef double a = 0.0, b = 1.0for i in range(n):a, b = a + b, areturn a
  1. 编译fib.pyx文件为动态链接库.so
    这里有两种编译方式,一种是使用setup.py自动进行编译,一种是手动进行编译。
    • setup.py文件

      from distutils.core import setup
      from Cython.Build import cythonizesetup(ext_modules = cythonize("fib.pyx"))
      

      然后在命令行运行python setup.py build_ext --inplace 便会在同级目录下生成一个以fib开头的动态链接库以及一个fib.c文件,这个fib.c文件就是fib.pyx完全转为c代码后结果。

    • 手动编译

      • 第一步:在命令行运行cython fib.pyx,会生成fib.c
      • 使用gcc对fib.c编译生成动态链接库: gcc -fPIC -shared -I ~/miniconda3/include/python3.11/ fib.c -o fib.so。注意这里python include的路径需要你自己更换为自己环境的路径。

这样在第2步,我们就生成了动态链接库.so文件。

  1. 在python代码中使用这个动态链接库

    import fibprint(fib.fib(100))
    

以上就是Cython工作的大体流程。这里要注意的是:我的介绍只是一点点入门知识,Cython还是很博大精深的。

在C中调用python代码

上面我们已经说过,Cython是python的超集,所以如果我们有一个python脚本或者模块,想要在C语言环境中调用它,那么可以使用cython对这个py文件进行编译生成动态链接库,然后在C语言中调用它即可。

注:这一种方式博主没有亲自测试过

调用Python的原生C API

这是最暴力的一种方法,我们知道,python这个语言也有C的API,所以我们可以直接在C语言代码中使用这些API来调用python模块,下面是一个简单的示例。

  1. 我们拥有的my_modules.py文件

    # # my_modules.py
    def add(a, b):print(a + b)return a + bdef helloworld(s):print("hello " + s)class A:def __init__(self, a, b) -> None:self.first = aself.second = bdef add(self):print(self.first+self.second)return "hello world"
    

    可以看到,有两个函数(一个做求和,一个输出"hello world")和一个类。

  2. 构建C++的环境
    我是使用cmake进行编译程序的,所以要配置好CMakeLists.txt,配置如下:

cmake_minimum_required(VERSION 3.16)
project(CallPython)find_package (Python COMPONENTS Interpreter Development)  # 找到python解释器
message(STATUS "Python_VERSION: ${Python_INCLUDE_DIRS}")
message(STATUS "python_LIBRARIES: ${Python_LIBRARIES}")
# message(STATUS "python_Interpreter: ${ython_LIBRARIES}")
# message(STATUS "python_LIBRARIES: ${ython_LIBRARIES}")
include_directories(${Python_INCLUDE_DIRS} )
# 生成目标文件
add_executable(call_python call_python.cpp)
# 链接库
target_link_libraries(call_python ${Python_LIBRARIES})
  1. 创建call_python.cpp文件,文件内容如下:
#include <iostream>
#include <Python.h>  // 必须要有这个头文件,在cmake中进行配置也是为了找到这个头文件int main(int argc, char** argv){// 运行Python解释器Py_Initialize();// 添加.py的路径PyRun_SimpleString("import sys");PyRun_SimpleString("sys.path.append('/home/wjq/workspace/test1')"); // py文件的父目录/*********************************** add函数 ************************************/// 导入模块PyObject* pModule = PyImport_ImportModule("my_modules"); // 导入要运行的函数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);    /*************************** ******class A测试*******************************/PyObject* pDict = PyModule_GetDict(pModule);// 类PyObject* pClassA = PyDict_GetItemString(pDict, "A");// 类的构造对象PyObject* pConstruct = PyInstanceMethod_New(pClassA);// 类的对象PyObject * pInsA = PyObject_CallObject(pConstruct, args);// 调用类的方法PyObject* result = PyObject_CallMethod(pInsA, "add", nullptr);// 对结果进行解读if(result != nullptr){char * str_result;PyArg_Parse(result, "s", &str_result);printf("Result: %s\n", str_result);  Py_DECREF(result);}// 终止Python解释器Py_Finalize();  }

结果如下所示:

11
result:11
hello python
11
Result: hello world

Python的C API有很多,这里我们只是用了几个,关于更多的API,请参考官网。

参考链接

  1. https://www.52txr.cn/2023/CPytonCython.html
  2. https://www.cnblogs.com/traditional/p/13196509.html
  3. https://chend0316.github.io/backend/cython/#%E7%AC%AC1%E7%AB%A0-cython%E7%9A%84%E5%AE%89%E8%A3%85%E5%92%8C%E4%BD%BF%E7%94%A8
  4. https://blog.csdn.net/u011722929/article/details/114871365
  5. https://www.hbblog.cn/python%26C%2B%2B/python%E5%92%8CC%E7%9A%84%E4%BA%A4%E4%BA%92/#31-pythonapi
  6. https://zhuanlan.zhihu.com/p/79896193
  7. https://blog.csdn.net/qq_42688495/article/details/120563844

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/720026.shtml

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

相关文章

山西电力市场日前价格预测【2024-02-27】

日前价格预测 预测说明&#xff1a; 如上图所示&#xff0c;预测明日&#xff08;2024-02-27&#xff09;山西电力市场全天平均日前电价为332.28元/MWh。其中&#xff0c;最高日前电价为544.59元/MWh&#xff0c;预计出现在19:00。最低日前电价为211.48元/MWh&#xff0c;预计…

如何在Linux使用Docker部署Redis并结合内网穿透实现公网远程连接本地数据库

文章目录 前言1. 安装Docker步骤2. 使用docker拉取redis镜像3. 启动redis容器4. 本地连接测试4.1 安装redis图形化界面工具4.2 使用RDM连接测试 5. 公网远程访问本地redis5.1 内网穿透工具安装5.2 创建远程连接公网地址5.3 使用固定TCP地址远程访问 正文开始前给大家推荐个网站…

还在手动Word转PPT?快来试试这些一键生成工具!

在我们日常的工作和学习中&#xff0c;将Word转化成PPT的需求时常出现&#xff0c;尤其是当我们需要进行演讲或者报告时。这不仅能使我们的演讲更具视觉冲击力&#xff0c;也有助于我们更好地传达信息。 那么&#xff0c;如何才能轻松地将Word转换成PPT呢&#xff1f;下面将为…

商家转账到零钱功能申请方法

商家转账到零钱是什么&#xff1f; 【商家转账到零钱】可以说是【企业付款到零钱】的升级版&#xff0c;商家转账到零钱可以为商户提供同时向多个用户微信零钱转账的能力&#xff0c;支持分销返佣、佣金报酬、企业报销、企业补贴、服务款项、采购货款等自动向用户转账的场景。…

Intel 芯片 Mac 如何重新安装系统

使用可引导安装器重新安装&#xff08;可用于安装非最新的 Mac OS&#xff0c;系统降级&#xff0c;需要清除所有数据&#xff0c;过程确保连接上网络&#xff0c;虽然这种方式不会下载 Mac OS&#xff0c;但是需要下载固件等信息&#xff09; 插入制作好的可引导安装器&#x…

leetcode 热题 100_找到字符串中所有字母异位词

题解一&#xff1a; 滑动窗口&#xff1a;类似于字符串匹配&#xff0c;但匹配异位词需要包含相同的字母及个数&#xff0c;可以分别用两个数组存储字符串s滑动窗口和字符串p的字母及个数&#xff0c;再用Array.equals()进行比对。对于s.length()<p.length()的情况需要特判。…

【python】python用户管理系统[简易版](源码+报告)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

怎么异地共享文件?

不同地点的团队成员之间共享文件是现代企业中常见的需求之一。随着分布式团队的不断增加&#xff0c;找到一种安全、高效的方式来实现异地共享文件变得尤为重要。本文将介绍一种名为【天联】的工具&#xff0c;它可以帮助团队成员在异地互相共享文件。 【天联】是一种专门为异地…

.NET Core Web API扩展框架

在.NET Core Web API中&#xff0c;你可以使用各种扩展框架和库来增强应用程序的功能和性能。这些扩展框架可以涵盖多个方面&#xff0c;包括认证与授权、异常处理、日志记录、API文档生成、性能监控等。以下是一些常用的.NET Core Web API扩展框架&#xff1a; 认证与授权 AS…

Corel 会声会影 2023 激活码 会声会影 2023 序列号生成器

会声会影 2023 已经出来很长时间了&#xff0c;但是对它的热爱一直持续不减&#xff0c;今天我给大家带来2023版本为用户带来的多个全新功能&#xff0c;可以更好的编辑视频&#xff0c;不过软件还是付费的&#xff0c;为此我带来了会声会影 2023序列号生成器&#xff0c;可以轻…

STM32 | STM32时钟分析、GPIO分析、寄存器地址查找、LED灯开发(第二天)

STM32 第二天 一、 STM32时钟分析 寄存器&#xff1a;寄存器的功能是存储二进制代码&#xff0c;它是由具有存储功能的触发器组合起来构成的。一个触发器可以存储1位二进制代码&#xff0c;故存放n位二进制代码的寄存器&#xff0c;需用n个触发器来构成 在计算机领域&#x…

数码管的动态显示(一)

1.原理 把每一个数码管闪烁的时间设置为1ms&#xff0c;肉眼观察不到就会认为6个数码管在同时闪烁。 实验目标&#xff1a; 使用6位8段数码管实现数码管的动态显示&#xff0c;显示的内容就是0-999_999。当计数到最大值&#xff0c;让他归零&#xff0c;然后循环显示。每0.1秒…

Doris2.0 部署流程、遇到的问题及1.0升级至2.0流程整理

背景 Doris 1.0 版本总是出现副本损坏问题&#xff0c;机器资源充足&#xff0c;FE 和 BE 数据足够&#xff0c;每日的数据量一般&#xff0c;但是总是隔三差五出现入库时副本损坏问题。Doris 已经发布了2.0 版本&#xff0c;本周又发布了新版本 2.0.5。升级 Doris 能否解决副…

Jenkins 安装

目录 1、部署 Jenkins 安装配置 Jenkins 解锁 Jenkins 安装 Jenkins 插件 创建管理员账号 手动安装插件 2、Jenkins 从 GitLat 拉取代码 安装 Jenkins 插件 在 node-16 上生成密钥对 把公钥配置到 gitlab 上 把 root 用户私钥配置到 jenkins 上 Jenkins 创建一个任务…

命名空间(namespace)

定义 在C中&#xff0c;命名空间&#xff08;Namespace&#xff09;是一个特性&#xff0c;用于封装代码并避免名称冲突。命名空间可以看作是一个容器&#xff0c;其中可以包含类、函数、变量、常量、其他命名空间等。通过使用命名空间&#xff0c;我们可以更好地组织代码&…

C#进阶高级语法之LINQ :Lambda 表达式

C# 中的 LINQ (Language Integrated Query) 提供了一种声明性的数据查询和操作方法&#xff0c;它允许开发人员对集合、数据库等数据源进行查询和操作&#xff0c;而不需要编写复杂的循环和手动编码。Lambda 表达式与 LINQ 紧密相关&#xff0c;它提供了一种简洁的方式来定义匿…

职场中“祖传代码”的处理建议

程序员是如何看待“祖传代码”的&#xff1f; 祖传代码的由来 在实际的程序员工作中&#xff0c;祖传代码是常见的。因为真正的互联网职场生活中&#xff0c;业务变动调整频繁&#xff0c;每到一个新的业务线&#xff0c;第一件事就是熟悉现有工程代码&#xff0c;看旧文档等…

面试经典150题 -- 回溯 (总结)

总的链接 : 面试经典 150 题 - 学习计划 - 力扣&#xff08;LeetCode&#xff09;全球极客挚爱的技术成长平台 17 . 电话号码的字母组合 1 . 先创建一个下标 与 对应字符串映射的数组&#xff0c;这里使用hash表进行映射也是可以的 &#xff1b; 2 . 对于回溯 &#xff0c;…

论人类饮食多样性的偏好:为何人们倾向于每一餐都吃得不同

人类的饮食行为深深植根于生物进化、文化传承和社会心理等多个维度。细心观察日常生活&#xff0c;我们会发现大部分人在饮食选择上有着显著的多样化倾向&#xff0c;即使不是餐餐大鱼大肉&#xff0c;也会尽可能让每一顿饭的内容有所不同。本文旨在深入探讨这种饮食多样化偏好…

下班前几分钟,我彻底弄懂了YAML

目录 1. YAML语法1.1 Scalars1.2 Array1.3 Object1.4 复杂结构1.5 高级用法1.5.1 浮点数特殊值1.5.2 锚点与别名1.5.3 合并键 2. PyYAML的使用2.1 解析YAML2.2 生成YAML 3. YAML和JSON的区别Ref 1. YAML语法 YAML&#xff08;YAML Ain’t Markup Language&#xff09;是一种用…