首页 > 编程语言 >C++/Python混合编程

C++/Python混合编程

时间:2022-10-05 20:46:35浏览次数:91  
标签:函数 pydemo Python 编程 模块 C++ pybind11

以 C++ 为底层基础,Python 作为上层建筑,共同搭建起高性能、易维护、可扩展的混合系统。

Python 本身就有 C 接口,可以用 C 语言编写扩展模块,把一些低效耗时的功能改用 C 实现,有的时候,会把整体性能提升几倍甚至几十倍。

使用 C++ 来开发 Python 扩展。认为其中最好的一个就是pybind11

pybind11 借鉴了“前辈”Boost.Python,能够在 C++ 和 Python 之间自由转换,任意翻译两者的语言要素,比如把 C++ 的 vector 转换为 Python 的列表,把 Python 的元组转换为 C++ 的 tuple,既可以在 C++ 里调用 Python 脚本,也可以在 Python 里调用 C++ 的函数、类。

pybind11 名字里的“11”表示它完全基于现代 C++ 开发(C++11 以上),所以没有兼容旧系统的负担。它使用了大量的现代 C++ 特性,不仅代码干净整齐,运行效率也更高。

接下来看下,如何用Pybind11,让C++辅助Python,提升Python的性能



pybind11是一个纯头文件的库,但因为必须结合Python,所以首先要有Python的开发库,然后在用pip工具安装。

pybind11 支持 Python2.7、Python3 和 PyPy,这里用的是 Python3:

apt-get install python3-dev
apt-get install python3-pip
pip3 install pybind11

pybind11 充分利用了 C++ 预处理和模板元编程,把原本无聊重复的代码都隐藏了起来,展现了“神奇的魔法”——只需要短短几行代码,就可以实现一个 Python 扩展模块。具体怎么实现呢?

只要用一个宏“PYBIND11_MODULE”,再给它两个参数,Python 模块名和 C++ 实例对象名,就可以了。

#include <pybind11/pybind11.h>        // pybind11的头文件

PYBIND11_MODULE(pydemo, m)            // 定义Python模块pydemo
{
  m.doc() = "pybind11 demo doc";      // 模块的说明文档
}                                     // Python模块定义结束

代码里的 pydemo 就是 Python 里的模块名,之后在 Python 脚本里必须用这个名字才能 import。

第二个参数“m”其实是 pybind11::module 的一个实例对象,封装了所有的操作,比如这里的 doc() 就是模块的说明文档。它只是个普通的变量,起什么名字都可以,但为了写起来方便,一般都用“m”。

假设这个 C++ 源文件名是“pybind.cpp”,现在你就可以用 g++ 把它编译成在 Python 里调用的模块了,不过编译命令比较复杂:

g++ pybind.cpp               \                  #编译的源文件
   -std=c++11 -shared -fPIC   \                 #编译成动态库
  `python3 -m pybind11 --includes` \            #获得包含路径
  -o pydemo`python3-config --extension-suffix`  #生成的动态库名字

第一行是指定编译的源文件,第二行是指定编译成动态库,
第三行调用了 Python,获得 pybind11 所在的包含路径,让 g++ 能够找得到头文件。第四行最关键,是生成的动态库名字,前面必须是源码里的模块名,而后面那部分则是 Python 要求的后缀名,否则 Python 运行时会找不到模块。

编译完后会生成一个大概这样的文件:pydemo.cpython-35m-x86_64-linux-gnu.so,现在就可以在 Python 里验证了,使用 import 导入,然后用 help 就能查看模块说明:

$ python3
>>> import pydemo
>>> help(pydemo)

刚才的代码非常简单,只是个空模块,里面什么都没有,现在来看看怎么把 C++ 的函数导入 Python

需要用的是 def() 函数,传递一个 Python 函数名和 C++ 的函数、函数对象或者是 lambda 表达式,形式上和 Python 的函数也差不多

namespace py = pybind11;               // 名字空间别名,简化代码

PYBIND11_MODULE(pydemo, m)             // 定义Python模块pydemo
{
  m.def("info",                        // 定义Python函数
    []()                              // 定义一个lambda表达式
    {
      py::print("c++ version =", __cplusplus);  // pybind11自己的打印函数
      py::print("gcc version =", __VERSION__);
      py::print("libstdc++   =", __GLIBCXX__);
    }
  );  

  m.def("add",                       // 定义Python函数
    [](int a, int b)                 // 有参数的lambda表达式
    {
      return a + b;
    }
  );  
}                                     // Python模块定义结束

在 Python 里可以验证效果:

import pydemo            # 导入pybind11模块
pydemo.info()            # 调用C++写的函数
x = pydemo.add(1,2)      # 调用C++写的函数

pybind11 也支持函数的参数、返回值使用标准容器,会自动转换成 Python 里的 list、dict,不过你需要额外再包含一个“stl.h”的头文件。

下面的示例代码演示了 C++ 的 string、tuple 和 vector 是如何用于 Python 的:

#include <pybind11/stl.h>              // 转换标准容器必须的头文件

PYBIND11_MODULE(pydemo, m)             // 定义Python模块pydemo
{
  m.def("use_str",                     // 定义Python函数
    [](const string& str)              // 入参是string
    {
        py::print(str);
        return str + "!!";            // 返回string
    }
  );  

  m.def("use_tuple",                  // 定义Python函数
    [](tuple<int, int, string> x)     // 入参是tuple
    {
        get<0>(x)++;
        get<1>(x)++;
        get<2>(x)+= "??";
        return x;                     // 返回元组
    }
  );  

  m.def("use_list",                   // 定义Python函数
    [](const vector<int>& v)          // 入参是vector
    {
        auto vv = v;
        py::print("input :", vv);
        vv.push_back(100);
        return vv;                    // 返回列表       
    }
  );
}

因为都是面向对象的编程语言,C++ 里的类也能够等价地转换到 Python 里面调用,这要用到一个特别的模板类 class_,注意,它有意模仿了关键字 class,后面多了一个下划线。

拿一个简单的 Point 类来举个例子:

class Point final
{
public:
    Point() = default;
    Point(int a);
public:
    int get() const;
    void set(int a);
};

使用 pybind11,你需要在模板参数里写上这个类名,然后在构造函数里指定它在 Python 里的名字。

导出成员函数还是调用函数 def(),但它会返回对象自身的引用,所以就可以连续调用,在一句话里导出所有接口:

py::class_<Point>(m, "Point")           // 定义Python类
    .def(py::init())                    // 导出构造函数
    .def(py::init<int>())               // 导出构造函数
    .def("get", &Point::get)            // 导出成员函数
    .def("set", &Point::set)            // 导出成员函数
    ;

对于一般的成员函数来说,定义的方式和普通函数一样,只是你必须加上取地址操作符“&”,把它写成函数指针的形式。而构造函数则比较特殊,必须调用 init() 函数来表示,如果有参数,还需要在 init() 函数的模板参数列表里写清楚

小试牛刀:可参考官方文档,学习其他具体方法。

参考:
https://time.geekbang.org/column/article/245905

标签:函数,pydemo,Python,编程,模块,C++,pybind11
From: https://www.cnblogs.com/whiteBear/p/16756299.html

相关文章

  • Python语法之类和对象
    这篇文章给大家介绍一下类和对象,类和对象是面向对象编程的核心概念。Python中的类,和我们生活的“物以类聚”类似,都是包含了这一类别内都具备的一些特征。Python中的类是......
  • python基础知识回顾
    与用户交互关键字input('需要用户输入的内容')在python2中,需要自己定义input输入的数据类型,在python3统一处理为字符串print('打印的内容)print自带换行......
  • 【c++基础】从json文件提取数据
    前言标注数据导出文件是json格式的,也就是python的dict格式,需要读取标注结果,可以使用c++或者python,本文使用c++实现的。JsonCpp简介JsonCpp是一种轻量级的数据交换格式,是个跨......
  • Python 使用smtplib、zmail或yagmail发送邮件
    Python中,可以使用通过代码方式发送邮件,本文主要介绍使用smtplib、zmail和yagmail三种方法发送邮件,以及相关的示例代码。原文地址:Python使用smtplib、zmail或yagmail发送......
  • 编程的学习总结
    一,实验代码   #include<stdio.h>intmain(){printf("Hello,world!")}  二,设计思路 第一步:打框架第二步:算法是排序   第三步:编译,查......
  • 对比python学julia(第四章:人工智能)--(第一节)OpenCV编程初步(3)
    1.4. 人脸检测(续上)3.检测视频中的人脸在VSCode环境中,新建一个空白源文件,以detect_video.jl作为文件名保存到项目文件夹中,然后编写程序检测视频流......
  • C++的基本数据类型
    C++的基本数据类型C++的数据类型可分为:1. 布尔类型:false0 true 12. 字符类型:可分为三种,char、signedchar、unsignedchar,其表现形式只是分为有符号和无符号,字符......
  • Python5-
    实战01(打印象棋口诀)1str1='马走日'2str2=','3str3='象走田'4str4=','5str5='车走直线炮翻山'6str6=','7str7='士走斜线护将边'8st......
  • EffectiveC++中文版(第三版) pdf
    高清扫描版下载链接:https://pan.baidu.com/s/1I1w2alIPQH1GJ32lz0MoaA点击这里获取提取码 ......
  • Linux多线程服务端编程 pdf
    高清文字版下载链接:https://pan.baidu.com/s/1Ar0sbiycp70BdNysXfkg2w点击这里获取提取码 ......