以 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

C++/Python混合编程的更多相关文章

  1. 在Qt(C++)中与Python混合编程

    一.PythonQt库 在Qt(C++)中与Python混合编程,可以使用PythonQt库. 网站首页:http://pythonqt.sourceforge.net 下载页面:https://so ...

  2. 很好的c++和Python混合编程文章

    c++中嵌入python入门1 本人是用vc2003+python2.5学习的,其它的也应该差不了多少 0. 坏境设置把Python的include/libs目录分别加到vc的include/lib ...

  3. C++/Python 混合编程 Clion IDE 搭建

    1.Clion IDE 环境 2.TdmGcc C++ 编译器 3.WinPython Python 科学计算环境 4.MySQL SQL 数据库 5.Git GitHub 版本控制 插件

  4. C语言调用Python 混合编程

    导语 Python有很多库,Qt用来编写界面,自然产生C++调用Python的需求.一路摸索,充满艰辛 添加头文件搜索路径,导入静态库 我的python头文件搜索路径:C:\Python27amd64 ...

  5. C++和Python混合编程

    为何人工智能(AI)首选Python?读完这篇文章你就知道了:https://blog.csdn.net/qq_41769259/article/details/79419322 C++调用Pytho ...

  6. 批处理与python代码混合编程的实现方法

    批处理可以很方便地和其它各种语言混合编程,除了好玩,还有相当的实用价值, 比如windows版的ruby gem包管理器就是运用了批处理和ruby的混合编写, bathome出品的命令工具包管理器bc ...

  7. Python和C++的混合编程(使用Boost编写Python的扩展包)

    想要享受更轻松愉悦的编程,脚本语言是首选.想要更敏捷高效,c++则高山仰止.所以我一直试图在各种通用或者专用的脚本语言中将c++的优势融入其中.原来贡献过一篇<c++和js的混合编程>也是 ...

  8. 使用 ctypes 进行 Python 和 C 的混合编程

    Python 和 C 的混合编程工具有很多,这里介绍 Python 标准库自带的 ctypes 模块的使用方法. 初识 Python 的 ctypes 要使用 C 函数,需要先将 C 编译成动态链接库 ...

  9. 【转载】ANSYS的APDL与C语言混合编程(实例)

    原文地址:http://www.cnblogs.com/lyq105/archive/2010/05/04/1727557.html 本文讨论的不是利用C语言为ANSYS写扩展(或者说是用户子程序), ...

  10. 用c/c++混合编程方式为ios/android实现一个自绘日期选择控件(一)

    本文为原创,如有转载,请注明出处:http://www.cnblogs.com/jackybu 前言 章节: 1.需求描述以及c/c++实现日期和月历的基本操作 2.ios实现自绘日期选择控件 3.a ...

随机推荐

  1. Typecho 如何开启 HTTPS

    一般来说,我们直接开启 HTTPS 就行,开启后进去网站后台修改网站的 URL 即可. 但是我昨天发现,我的工具箱迁移服务器之后,前台看着是很正常的,但是后台的登陆页面引入的依然的 http 标头,所 ...

  2. 八米云-N1、机顶盒设置静态地址和PPPOE拨号流程

    疑难解答加微信机器人,给它发:进群,会拉你进入八米交流群 机器人微信号:bamibot 简洁版教程访问:https://bbs.8miyun.cn 这里以老毛子路由系统举例: 一.设置静态地址 1.路 ...

  3. 网线 - 568B接法

  4. 大量小文件不适合存储于HDFS的原因

    1.小文件过多,会过多占用namenode的内存,并浪费block. - 文件的元数据(包括文件被分成了哪些blocks,每个block存储在哪些服务器的哪个block块上),都是存储在namenod ...

  5. git ssh秘钥生成及配置

    ssh-keygen -t rsa -C "email@gmail.com" 回车后提示输入密码, 此处密码可以不填, 直接回车,提示再次输入密码, 直接回车,生成成功 前往 /U ...

  6. 自动化平台-环境搭建2-cmd 下mysql 卸载命令

    "" net stop mysql sc delete mysql rd /s /q "C:\Program Files\MySQL" rd /s /q &qu ...

  7. 多智能体粒子环境(Multi-Agent Particle Env)食用指南--从入门到入土

    0.项目地址: 原地址:openai/multiagent-particle-envs: Code for a multi-agent particle environment used in the ...

  8. kubernetes安装配置使用vGPU

    前言 AI 落地时,在某些场景下 AI 模型在训练或者是推理时,其算力要求不需要占用整卡的 GPU,比如只需要0.5卡 GPU 即可满足需求. 在这种情况下,可以使用 GPU 虚拟化技术来解决这个问题 ...

  9. Centos安装完成后,ifconfig:command not found

    1.问题:在刚最小安装完centos7,想查看本机IP地址.然后运行ifconfig,结果弹出报错. 2.问题排查:首先我们了解是不是没有开启网卡导致的?我们可以通过一下3种方法来排查: 1)通过pi ...

  10. 一文速通Python并行计算:02 Python多线程编程-threading模块、线程的创建和查询与守护线程

    一文速通 Python 并行计算:02 Python 多线程编程-threading 模块.线程的创建和查询与守护线程 摘要: 本文介绍了 Python threading 模块的核心功能,包括线程创 ...