Pybind11简单入门

Pybind11

快速入手:https://www.bilibili.com/video/BV1oD4y1Y7YH/?spm_id_from=333.337.search-card.all.click&vd_source=7fc00062d9cd78f73503c26f05fad664

简介

pybind11——C++11和Python之间的无缝可操作性

pybind11是C++的header-only的库。因此无需安装,只需要有头文件即可。它在Python中公开C++类型,反之亦然,主要用于创建现有C++代码的Python绑定。

Pybind11安装

克隆Pybind11库并删掉.git和.github文件

# 克隆源码
git clone https://github.com/pybind/pybind11.git

Linux安装Pybind

# 进入pybind11目录
cd pybind11
# 构建
mkdir build 
cd build 
# 编译安装
cmake ..
make check -j 4
sudo make install

pybind的规范使用

为了简洁起见,所有代码示例都假设存在以下两行:

#include <pybind11/pybind11.h>

namespace py = pybind11;

Pybind11使用

函数使用

为简单函数创建绑定

从一个极其简单的函数创建Python绑定开始,该函数将两个数字相加并返回结果,将把这个函数和绑定代码放在一个名为example.cpp的文件中,文件内容如下:

#include <pybind11/pybind11.h>

int add(int i, int j) {
    return i + j;
}

PYBIND11_MODULE(example, m) {
    m.doc() = "pybind11 example plugin"; // 可选模块

    m.def("add", &add, "A function that adds two numbers");
}

PYBIND11_MODULE()宏创建一个函数,当从Python中发出“import”语句时将调用该函数。

  • 第一个参数为模块名称(example)是作为第一个宏参数给出的(不应该用引号括起来)。
  • 第二个参数(m)定义了一个类型为py::module_的变量,它是创建绑定的主要接口。

方法module_::def()生成绑定代码,将“add()”函数公开给Python。

pybind11是一个只有头的库,因此不需要链接任何特殊的库,也没有中间翻译步骤。在Linux上,可以使用以下命令编译上述示例:

$ c++ -O3 -Wall -shared -std=c++11 -fPIC $(python3 -m pybind11 --includes) example.cpp -o example$(python3-config --extension-suffix)

python_examplecmake_example 存储库也是一个很好的起点。它们都是跨平台构建系统的完整项目示例。

两者之间唯一的区别是python_example使用Python的“setuptools”来构建模块,而cmake_example使用CMake(这对于现有的C++项目来说可能更可取)。

构建C++代码将生成一个可以导入Python的二进制模块文件。假设编译后的模块位于当前目录中,下面的交互式Python会话将显示如何加载和执行该示例:

$ python
>>> import example
>>> example.add(1, 2)
3

关键字参数

通过简单的代码修改,就可以向Python通知参数的名称(在本例中为“i”和“j”)。

m.def("add", &add, "A function which adds two numbers",
      py::arg("i"), py::arg("j"));

arg是几个特殊标记类之一,可用于将元数据传递到module_::def(). 有了这个修改后的绑定代码,我们现在可以使用关键字arguments调用函数,这是一个更可读的替代方法,尤其是对于使用许多参数的函数:

>>> import example
>>> example.add(i=1, j=2)
3L

关键字名称也出现在文档中的函数签名中:

>>> help(example)

....

FUNCTIONS
    add(...)
        Signature : (i: int, j: int) -> int

        A function which adds two numbers

还提供了命名参数的较短表示法:

// 规范命名
m.def("add1", &add, py::arg("i"), py::arg("j"));
// 简短命名
using namespace pybind11::literals;
m.def("add2", &add, "i"_a, "j"_a);

_ a后缀形成一个相当于arg的C++11文字. 请注意,文字运算符必须首先通过 using namespace pybind11::literals; 指令可见。这不会从“pybind11”命名空间中引入除文字以外的任何其他内容。

默认参数

现在假设要绑定的函数具有默认参数,例如:

int add(int i = 1, int j = 2) {
    return i + j;
}

pybind11无法自动提取这些参数,因为它们不是函数类型信息的一部分。但是,使用arg的扩展名很容易指定它们:

m.def("add", &add, "A function which adds two numbers",
      py::arg("i") = 1, py::arg("j") = 2);

默认值也显示在文档中。

>>> help(example)

....

FUNCTIONS
    add(...)
        Signature : (i: int = 1, j: int = 2) -> int

        A function which adds two numbers

速记符号也可用于默认参数:

// 规范命名
m.def("add1", &add, py::arg("i") = 1, py::arg("j") = 2);
// 简短命名
m.def("add2", &add, "i"_a=1, "j"_a=2);

导出变量

要公开C++中的值,请使用“attr”函数将其注册到模块中,如下所示。内置类型和常规对象(稍后将详细介绍)在指定为属性时会自动转换,并且可以使用函数“py::cast”显式转换。

PYBIND11_MODULE(example, m) {
    m.attr("the_answer") = 42;
    py::object world = py::cast("World");
    m.attr("what") = world;
}

然后可以从Python访问这些:

>>> import example
>>> example.the_answer
42
>>> example.what
'World'

简单的面向对象代码绑定使用

为自定义类型创建绑定

/*
	使用了PYBIND11库用来在C++和Python之间创建绑定。
	在下面代码中,创建了一个名为"TrafficDet"的Python类,该类在C++中对应的实体是"TrafficDet"。
*/ 
PYBIND11_MODULE(trafficdet, m) { // 此宏在PYBIND11库中定义,它创建一个Python模块,第一个参数为模块名为"trafficdet",第二个参数不用管是m就可以。
    py::class_<SmartHomeLib>(m, "SmartHomeLib") // 定义了一个Python类,其名称是"SmartHomeLib",这个类对应于C++中的"SmartHomeLib"类型。
        .def("detect", &TrafficDet::detect_yolov3); // 定义了一个Python方法,其名称是"detect",这个方法对应于C++中的"TrafficDet::detect_yolov3"方法。
}

例子:

我们将为名为“Pet”的自定义C++数据结构创建绑定。其定义如下:

// Pet 结构体定义了一个具有名称属性的简单宠物对象,并提供了设置和获取该名称的方法。
struct Pet { // 定义一个名字为Pet的结构体
    Pet(const std::string &name) : name(name) { } // 构造函数。当创建一个 Pet 对象时,它会被调用。这个构造函数接受一个 std::string 类型的参数 name,并使用初始化列表来初始化成员变量name。初始化列表是一种高效的初始化成员变量的方法。
    void setName(const std::string &name_) { name = name_; } // 成员函数,用于设置 Pet 对象的 name 属性。它接受一个常量字符串引用作为参数,并将其值赋给成员变量 name。但请注意,此函数有一个错误:它应该使用 name = name_ 而不是 name = name_(后者是一个自赋值,不会改变 name 的值)。
    const std::string &getName() const { return name; } // 成员函数,用于获取 Pet 对象的 name 属性的值。由于它返回一个常量字符串引用,所以调用者不能直接修改返回的字符串。此外,函数后面的 const 关键字表示这是一个常量成员函数,它不能修改结构体的任何成员变量(除了那些被声明为 mutable 的)。

    std::string name; // 成员变量,类型为 std::string,用于存储名字。
};

Pet的绑定代码如下所示:

#include <pybind11/pybind11.h>

namespace py = pybind11; // 设置命名空间

PYBIND11_MODULE(example, m) { // 此宏在PYBIND11库中定义,它创建一个Python模块,第一个参数为模块名为"example",第二个参数不用管是m就可以。
    py::class_<Pet>(m, "Pet") // class_为C++*类*或*结构*风格的数据结构创建绑定。定义了一个Python类,其名称是"Pet",这个类对应于C++中的"Pet"类型。
        .def(py::init<const std::string &>()) // init()是一个方便的函数,它将构造函数的参数类型作为模板参数,并包装相应的构造函数
        .def("setName", &Pet::setName) // 定义了一个Python方法,其名称是"setName",这个方法对应于C++中
        .def("getName", &Pet::getName); // 定义了一个Python方法,其名称是"getName",这个方法对应于C++中
}

演示此示例的交互式Python会话如下所示:

>>> import example
>>> p = example.Pet("Molly")
>>> print(p)
<example.Pet object at 0x10cd98060>
>>> p.getName()
'Molly'
>>> p.setName("Charly")
>>> p.getName()
'Charly'

打包

使用Cmake打包

参考:https://github.com/pybind/cmake_example

这对于具有现有CMake项目结构的C++代码库非常有用。

这将被scikit_build_example取代,使用scikit构建核心,旨在允许在不依赖于setuptools的情况下从CMake驱动Python包。

这里的方法有一些纯setuptools构建中没有的权衡(请参见python_example)或scikit构建核心。需要Python 3.7+;

条件
  • 一个支持C++11的编译器
  • Pip 10+或CMake>=3.4(在Windows上为3.14+,这是第一个支持VS 2019的版本)
  • Ninja or Pip 10+
安装

只需克隆此存储库并安装pip即可。注意pybind11子模块所需的“–recursive”选项(可以将pybind11文件夹补全):

git clone --recursive https://github.com/pybind/cmake_example.git
pip install ./cmake_example

在本例中包含“setup.py”文件的情况下,“pip-install”命令将调用CMake并按照“CMakeLists.txt”中的指定构建pybind11模块。

建立文档

示例项目的文档是使用Sphinx生成的。Sphinx能够自动检查扩展模块中的签名和文档字符串,以生成各种格式的漂亮文档。以下命令生成基于HTML的参考文档;其他格式请参考Sphinx 手册:

  • cd cmake_example/docs
  • make html
Test
import cmake_example
cmake_example.add(1, 2)

使用Python打包

参考:https://github.com/pybind/python_example

需要Python 3.7+;对于较旧版本的Python,请检查提交历史记录。

安装
git clone https://github.com/pybind/python_example.git
pip install ./python_example
CI 示例

在.github/workflows中有CI的示例。

在“wheels.yml”文件中,使用cibuildwheel说明了为所有平台生成二进制“wheels”的简单方法.

在“pip.yml”中看到构建和测试的基本配方,

在“conda.yml”有一个conda配方构建的示例。

建立文档

示例项目的文档是使用Sphinx生成的。Sphinx能够自动检查扩展模块中的签名和文档字符串,以生成各种格式的漂亮文档。以下命令生成基于HTML的参考文档;其他格式请参考Sphinx 手册:

  • cd python_example/docs
  • make html
测试
import python_example
python_example.add(1, 2)
  • 17
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值