pybind11学习笔记
myetyet

介绍

pybind11是一个轻量级的仅含头文件的C++库,用简单的语法为Python创建拓展模块,以加速运行。官方介绍可参考官方文档

安装

Windows端

  • 环境配置
    • Windows 10 家庭版 64位
    • Python 3.8.0 [MSC v.1916 32 bit (Intel)] on win32
    • gcc (MinGW.org GCC-8.2.0-3) 8.2.0
    • g++ (MinGW.org GCC-8.2.0-3) 8.2.0
    • pybind11 v2.6.2
  • 从官方仓库的发布页下载pybind11的源码。
  • 解压下载的压缩包,将其中的include文件夹单独放置。

    Linux端

  • Baidu AI Studio上注册帐户并登录。
  • 点击上方菜单栏中的[项目],点击[创建项目]。
  • 选择类型:选择[Notebook]类型,点击[下一步]。
  • 配置环境:项目框架选择[PaddlePaddle 2.0.0],项目环境选择[python 3.7],点击[下一步]。
  • 项目环境:三个项目均可随意设置,点击[创建]。
  • 启动环境:选择[基础版 (免费使用)],点击[确定]。
  • 待提示“环境启动成功”后点击[进入],进入环境。
  • 点击菜单栏下方的[终端-1],切换至终端选项卡,并依次执行以下命令:
    1
    2
    3
    mkdir pybind
    cd pybind
    git clone https://github.com.cnpmjs.org/pybind/pybind11.git

编写测试程序

Windows端

  • include文件夹下创建myetyet.cpp文件,编写如下内容后保存。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #include <pybind11/pybind11.h>
    namespace py = pybind11;

    int add(int i, int j) {
    return i + j;
    }
    PYBIND11_MODULE(myetyet, m) {
    m.doc() = "myetyet's pybind11 test module";
    m.def("add", &add, "A function which adds two integers.");
    }
  • 重复上述过程,创建setup.py文件,编写如下内容后保存。
    1
    2
    3
    from setuptools import setup, Extension

    setup(ext_modules=[Extension(name="myetyet", sources=["myetyet.cpp"], include_dirs=["."])])
  • 在终端依次执行以下命令:
    1
    2
    python setup.py build_ext -c mingw32 --inplace
    dir | find "pyd"
  • 若终端给出形如myetyet.cp38-win32.pyd的信息,即代表测试程序编译完成。
  • 继续在终端依次执行python命令。
  • 在Python交互式终端中依次执行以下语句:
    1
    2
    3
    4
    import os
    os.add_dll_directory("C:\\MinGW\\bin")
    import myetyet
    myetyet.add(233, 666)
  • 注意,此处的C:\\MinGW\\bin应为pyd文件依赖的动态链接库的所在目录,一般为MinGW安装目录的bin文件夹。
  • 若终端先后给出<AddedDllDirectory('C:\\MinGW\\bin')>899的信息,即代表测试程序可以正确运行。
  • 在Python交互式终端中输入exit()退出。

Linux端

  • 点击左侧侧边栏上方第四个按钮[刷新]。
  • 随后将在侧边栏中刷新出刚创建的pybind文件夹,点击之进入。
  • 点击上方第一个按钮[新建文件],名称为myetyet.cpp,点击[确定]。
  • 点击侧边栏中的myetyet.cpp,在右侧代码编辑区中输入以下代码后保存。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #include <pybind11/pybind11.h>
    namespace py = pybind11;

    int add(int i, int j) {
    return i + j;
    }
    PYBIND11_MODULE(myetyet, m) {
    m.doc() = "myetyet's pybind11 test module";
    m.def("add", &add, "A function which adds two integers.");
    }
  • 重复上述过程,创建CMakeLists.txt文件,输入以下代码后保存。
    1
    2
    3
    4
    5
    cmake_minimum_required(VERSION 3.4)
    project(myetyet LANGUAGES CXX)
    set(CMAKE_CXX_STANDARD 14)
    add_subdirectory(pybind11)
    pybind11_add_module(myetyet myetyet.cpp)
  • 在终端依次执行以下命令:
    1
    2
    3
    4
    5
    mkdir build
    cd build
    cmake ..
    make
    ls | grep so
  • 若终端给出形如myetyet.cpython-37m-x86_64-linux-gnu.so的信息,即代表测试程序编译完成。
  • 继续在终端依次执行python命令。
  • 在Python交互式终端中依次执行以下语句:
    1
    2
    import myetyet
    myetyet.add(233, 666)
  • 若终端给出899的信息,即代表测试程序可以正确运行。
  • 在Python交互式终端中输入exit()退出。
  • 方便起见,在pybind文件夹下创建build.sh。在终端依次执行以下命令:
    1
    2
    3
    cd ~/pybind
    echo -e "if [ ! -d \"./build\" ]; then\n mkdir build\nfi" > build.sh
    echo -e "cd build\ncmake ..\nmake\ncp *.so ../" >> build.sh
  • 之后对myetyet.cpp进行修改后则可在pybind文件夹下执行sh build.sh命令进行自动编译并复制.so文件至该目录。
  • 执行python命令进入Python交互式终端后,则可直接使用import myetyet进行模块的导入。

pybind11语法

  • 基础

    • 引用头文件及命名空间
      • C++侧
        1
        2
        #include <pybind/pybind11>
        namespace py = pybind11;
    • 创建模块
      • C++侧
        1
        PYBIND11_MODULE(module_name, m) { ... }
      • 备注
        • 严格意义上该条语法并不是函数定义,而是宏定义。
      • 参数
        • module_name:模块名(切勿加引号)
        • m:主要接口(py::module_型变量)
    • 属性
      • C++侧
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        // Normal types of variables exporting
        m.attr("a_bool") = true;
        m.attr("a_char") = 'A';
        m.attr("a_float") = 6.66;
        m.attr("an_int") = 233;
        m.attr("a_string") = py::cast("myetyet");
        // Include in advance
        #include <pybind11/stl.h>
        #include <vector>
        // STL container variables exporting
        m.attr("a_vector") = py::cast(std::vector<int> {233, 666});
      • Python侧
        1
        2
        3
        4
        5
        6
        7
        from myetyet import *
        a_bool, type(a_bool) # Output: (True, <class 'bool'>)
        a_char, type(a_char) # Output: ('A', <class 'str'>)
        a_float, type(a_float) # Output: (6.66, <class 'float'>)
        an_int, type(an_int) # Output: (233, <class 'int'>)
        a_string, type(a_string) # Output: ('myetyet', <class 'str'>)
        a_vector, type(a_vector) # Output: ([233, 666], <class 'list'>)
    • 枚举类型
      • C++侧
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        // Structure wrapper and inner enumerations definition
        struct Enums {
        enum DialogResult { Cancel = 2, OK = 0, Abort, Retry = 4 };
        };
        // Structure wrapper exporting
        py::class_<Enums> pyEnums(m, "Enums");
        // Enumerations exporting
        py::enum_<Enums::DialogResult>(pyEnums, "DialogResult")
        .value("Cancel", Enums::DialogResult::Cancel)
        .value("OK", Enums::DialogResult::OK)
        .value("Abort", Enums::DialogResult::Abort)
        .value("Retry", Enums::DialogResult::Retry)
        .export_values();
      • Python侧
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        from myetyet import *
        Enums.DlgRes.OK # Output: <DlgRes.OK: 0>
        str(Enums.DlgRes.Abort) # Output: 'DlgRes.Abort'
        int(Enums.DlgRes.Cancel) # Output: 2
        from pprint import pprint
        pprint(Enums.DlgRes.__members__) # Output as follows:
        """
        {'Abort': <DlgRes.Abort: 1>,
        'Cancel': <DlgRes.Cancel: 2>,
        'OK': <DlgRes.OK: 0>,
        'Retry': <DlgRes.Retry: 4>}
        """
  • 函数

    • 简单函数

      • C++侧
        1
        2
        3
        4
        5
        6
        7
        8
        // Function definition
        int add(int i, int j) {
        return i + j;
        }
        // Function exporting with referencing a defined function
        m.def("add", &add, "A function which adds two integers.");
        // Function exporting with a lambda function
        m.def("mod", [](int i, int j) { return i % j; }, "C++ mod function");
      • Python侧
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        help(myetyet.mod) # Output as follows:
        """
        Help on built-in function mod in module myetyet:

        mod(...) method of builtins.PyCapsule instance
        mod(arg0: int, arg1: int) -> int

        C++ mod function
        """
        myetyet.add(666, 233) # Output: 899
        myetyet.mod(666, 233) # Output: 200
    • 关键字参数

      • C++侧
        1
        2
        3
        4
        5
        6
        7
        8
        9
        // Function definition
        int _xor(int x, int y) {
        return x ^ y;
        }
        // Regular notation
        m.def("xor", &_xor, py::arg("x"), py::arg("y"));
        // Shorthand
        using namespace pybind11::literals;
        m.def("xor_", &_xor, "x"_a, "y"_a);
      • Python侧
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        help(myetyet.div) # Output as follows:
        """
        Help on built-in function xor in module myetyet:

        xor(...) method of builtins.PyCapsule instance
        xor(x: int, y: int) -> int
        """
        myetyet.xor(x=666, y=233) # Output: 627
        myetyet.xor(y=233, x=666) # Output: 627
        help(myetyet.xor) # Output as follows:
        """
        Help on built-in function xor_ in module myetyet:

        xor_(...) method of builtins.PyCapsule instance
        xor_(x: int, y: int) -> int
        """
        myetyet.xor_(666, y=233) # Output: 627
    • 参数默认值

      • C++侧
        1
        2
        3
        4
        5
        // Regular notation
        m.def("xor", &_xor, py::arg("x") = 666, py::arg("y") = 233);
        // Shorthand
        using namespace py::literals;
        m.def("xor_", &_xor, "x"_a = 666, "y"_a = 233);
      • Python侧
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        help(myetyet.xor) # Output as follows:
        """
        Help on built-in function xor in module myetyet:

        xor(...) method of builtins.PyCapsule instance
        xor(x: int = 666, y: int = 233) -> int
        """
        myetyet.xor(y=666) # Output: 0
        myetyet.xor(233) # Output: 0
        help(myetyet.xor_) # Output as follows:
        """
        Help on built-in function xor_ in module myetyet:

        xor_(...) method of builtins.PyCapsule instance
        xor_(x: int = 666, y: int = 233) -> int
        """
        myetyet.xor_() # Output: 627
    • 重载

      • C++侧
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        // Function definition
        double add(double x, double y) { return x + y; }
        string add(string x, string y) { return x + y; }

        // Function exporting with a lambda function
        m.def("add", [](int x, int y){ return x + y; }, "x"_a, "y"_a);
        // Function exporting with referencing a defined function
        // C++ 11 style
        m.def("add", static_cast<double (*)(double, double)>(&add), "x"_a, "y"_a);
        // C++ 14 style
        m.def("add", py::overload_cast<string, string>(&add), "x"_a, "y"_a);
      • Python侧
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        help(myetyet.add) # Output as follows:
        """
        Help on built-in function add in module myetyet:

        add(...) method of builtins.PyCapsule instance
        add(*args, **kwargs)
        Overloaded function.

        1. add(x: float, y: float) -> float

        2. add(x: str, y: str) -> str

        3. add(x: int, y: int) -> int
        """
        myetyet.add(2.33, 6.66) # Output: 8.99
        myetyet.add(233, 666) # Output: 899
        myetyet.add("myet", "yet") # Output: 'myetyet'
  • 自定义类

    • 构造函数与简单函数

      • C++侧
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        // Include in advance
        #include <string>
        using namespace std;
        // Structure definition
        struct Person {
        string name;
        Person(const string &name) : name(name) { }
        const string &getName() const { return name; }
        void setName(const string &name) { this->name = name; }
        };
        // Structure exporting
        py::class_<Person>(m, "Person")
        .def(py::init<const string &>(), "Initialize", "name"_a = "")
        // Function exporting with referencing a defined function
        .def("getName", &Person::getName, "Get name")
        .def("setName", &Person::setName, "Set name", "name"_a);
        // Function exporting with a lambda function
        .def("__repr__", [](const Person &p) { return "<a Person named '" + p.name + "'>"; });
      • Python侧
        1
        2
        p = myetyet.Person("123"); p # Output: <a Person named '123'>
        p.setName("myetyet"); p.getName() # Output: 'myetyet'
    • 属性

      • C++侧
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        // Structure definition
        struct Person {
        string name;
        const string ID = "233666";
        Person(const string &name) : name(name) { }
        const string &getName() const { return name; }
        void setName(const string &name) { this->name = name; }
        const string &getID() const { return ID; }
        };
        // Structure exporting
        py::class_<Person>(m, "Person")
        // Read/Write property
        .def_readwrite("name", &Person::name)
        // Or apply following 1 choice when a property is not public
        // .def_property("name", &Person::getName, &Person::setName)

        // Readonly property
        .def_readonly("ID", &Person::ID)
        // Or apply following 2 choices when a property is not public
        // .def_property("ID", &Person::getID, nullptr)
        // .def_property_readonly("ID", &Person::getID)

        .def(py::init<const string &>(), "Initialize", "name"_a = "");
      • Python侧
        1
        2
        3
        4
        5
        6
        7
        8
        9
        p = myetyet.Person("myetyet")
        p.name, p.ID # Output: ('myetyet', '233666')
        p.name = "myetyet_"; p.name # Output: 'myetyet_'
        p.ID = "666666" # Output as follows:
        """
        Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
        AttributeError: can't set attribute
        """
    • 动态属性

      • C++侧
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        // Class definition
        class Person {
        public:
        Person(const string &name) : name(name) { }
        const string &getName() const { return name; }
        void setName(const string &name) { this->name = name; }
        private:
        string name;
        const string ID = "233666";
        };
        // Class exporting
        py::class_<Person>(m, "Person", py::dynamic_attr())
        .def_readwrite("name", &Person::name)
        .def(py::init<const string &>(), "Initialize", "name"_a = "");
      • Python侧
        1
        2
        3
        p = myetyet.Person("myetyet")
        p.ID = 233666; p.url = "https://myetyet.js.org/"
        p.__dict__ # Output: {'ID': 233666, 'url': 'https://myetyet.js.org/'}
    • 继承

      • C++侧
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        // Structure definition
        struct Person {
        string name;
        Person(const string &name) : name(name) { }
        };
        struct Student : Person {
        Student(const string &name): Person{name} { }
        string loaf() { return "mo yu"; }
        };
        // Structure exporting
        py::class_<Person>(m, "Person")
        .def_readwrite("name", &Person::name)
        .def(py::init<const string &>(), "Initialize Person", "name"_a = "");
        py::class_<Student, Person>(m, "Student")
        .def("loaf", &Student::loaf)
        .def(py::init<const string &>(), "Initialize Student", "name"_a = "");
      • Python侧
        1
        2
        p = Student("myetyet")
        p.name, p.loaf() # Output: ('myetyet', 'mo yu')
    • 函数重载

      • C++侧
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        // Structure definition
        struct Vector2 {
        double x, y;
        Vector2() : x(0.0), y(0.0) {}
        Vector2(double x, double y) : x(x), y(y) {}
        void set(char lbl, double val) { switch (lbl) { case 'x': x = val; break; case 'y': y = val; break; } }
        void set(double x, double y) { this->x = x; this->y = y; }
        };
        // Structure exporting
        py::class_<Vector2>(m, "Vector2")
        // Constructor overloading
        .def(py::init<>())
        .def(py::init<double, double>(), "x"_a, "y"_a)
        // Member function overloading
        // C++ 11 style
        .def("set", static_cast<void (Vector2::*)(double, double)>(&Vector2::set), "x"_a, "y"_a)
        // C++ 14 style
        .def("set", py::overload_cast<char, double>(&Vector2::set), "label"_a, "value"_a)
        // Class display info
        .def("__repr__", [](const Vector2 &v) { return "<2D Vector (" + to_string(v.x) + ", " + to_string(v.y) + ")>"; });
      • Python侧
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        help(myetyet.Vector2.__init__) # Output as follows:
        """
        Help on instancemethod in module myetyet:

        __init__(...)
        __init__(*args, **kwargs)
        Overloaded function.

        1. __init__(self: myetyet.Vector2) -> None

        2. __init__(self: myetyet.Vector2, x: float, y: float) -> None
        """
        v = myetyet.Vector2(); v # Output: <2D Vector (0.000000, 0.000000)>
        v = myetyet.Vector2(.1, .2); v # Output: <2D Vector (0.100000, 0.200000)>
        help(myetyet.Vector2.set) # Output as follows:
        """
        Help on instancemethod in module myetyet:

        set(...)
        set(*args, **kwargs)
        Overloaded function.

        1. set(self: myetyet.Vector2, x: float, y: float) -> None

        2. set(self: myetyet.Vector2, label: str, value: float) -> None
        """
        v.set(6.66, 2.33); v # Output: <2D Vector (6.660000, 2.330000)>
        v.set("x", 6.1234575); v # Output: <2D Vector (6.123457, 2.330000)>
        v.set("y", 6.1234585); v # Output: <2D Vector (6.123457, 6.123458)>
    • 重载运算符

      • C++侧
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        // Structure definition
        struct Vector2 {
        double x, y;
        Vector2(double x, double y) : x(x), y(y) {}
        Vector2 operator-() const { return Vector2(-x, -y); }
        Vector2 operator*(double k) const { return Vector2(k * x, k * y); }
        Vector2 &operator*=(double k) { x *= k; y *= k; return *this; }
        double operator*(const Vector2 &b) const { return x * b.x + y * b.y; }
        friend Vector2 operator*(double k, const Vector2 &v) { return Vector2(k * v.x, k * v.y); }
        };
        // Structure exporting
        py::class_<Vector2>(m, "Vector2")
        .def(py::init<double, double>(), "x"_a, "y"_a)
        .def(-py::self)
        .def(py::self * double())
        .def(py::self *= double())
        .def(py::self * py::self)
        .def(double() * py::self)
        .def("__add__", [](const Vector2 &a, const Vector2 &b) { return Vector2(a.x + b.x, a.y + b.y); }, py::is_operator())
        .def("__repr__", [](const Vector2 &v) { return "<2D Vector (" + to_string(v.x) + ", " + to_string(v.y) + ")>"; });
      • Python侧
        1
        2
        3
        4
        5
        6
        7
        8
        9
        a = myetyet.Vector2(3, 4)
        -a # Output: <2D Vector (-3.000000, -4.000000)>
        a * 2 # Output: <2D Vector (6.000000, 8.000000)>
        a *= -2.0; a # Output: <2D Vector (-6.000000, -8.000000)>
        b = myetyet.Vector2(1, 2)
        a * b # Output: -22.0
        -.3 * a # Output: <2D Vector (1.800000, 2.400000)>
        a + b # Output: <2D Vector (-5.000000, -6.000000)>
        2 * a + b * 3 # Output: <2D Vector (-9.000000, -10.000000)>
  • 本文标题:pybind11学习笔记
  • 本文作者:myetyet
  • 创建时间:2021-02-01 13:40:56
  • 本文链接:https://myetyet.github.io/posts/dc90fd13/
  • 版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!
 评论