pybind11學習筆記

安裝

git clone --depth=1 https://github.com/pybind/pybind11
cd pybind11
mkdir build 
sudo make install

參考文檔

https://pybind11.readthedocs.io/en/stable/basics.html

第一個簡單示例

example.cpp

#include <pybind11/pybind11.h>

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

PYBIND11_MODULE(example, m) {
    m.doc() = "pybind11 example plugin"; // optional module docstring

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

CMakeLists.txt

cmake_minimum_required(VERSION 3.5)

project(example LANGUAGES CXX)

find_package(pybind11 REQUIRED)
include_directories(/usr/local/include)

pybind11_add_module(example example.cpp)

python

>>> import example
>>> example.add(3, 4)
>>> help(example)

添加默認參數

#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 docstring

    m.def("add", &add, "A function which adds two numbers", 
    	py::arg("i") = 1, py::arg("j") = 2 );
}
>>> import example
>>> example.add()
3
>>> example.add(j = 5)
6

以下代碼效果相同

#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 docstring

    using namespace pybind11::literals;
    m.def("add", &add,  "i"_a = 8, "j"_a = 10);
}

導出變量

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

類的使用

#include <pybind11/pybind11.h>
#include <string>

namespace py = pybind11;

struct Pet
{
    Pet(const std::string &name):name(name){}
    void setName(const std::string &name_)
    {
        name = name_;
    }
    const std::string &getName()
    {
        return name;
    }

    std::string name;
};

PYBIND11_MODULE(example, m)
{
    py::class_<Pet>(m, "Pet")
            .def(py::init<const std::string &>())
            .def("setName", &Pet::setName)
            .def("getName", &Pet::getName);
}

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

修改類註釋

PYBIND11_MODULE(example, m)
{
    py::class_<Pet>(m, "Pet")
            .def(py::init<const std::string &>())
            .def("setName", &Pet::setName)
            .def("getName", &Pet::getName)
            .def("__repr__",
                 [](const Pet &a) {
                    return "<example.Pet named '"+ a.name +"'>";
                 });
}
>>> import example
>>> p = example.Pet("Luccy")
>>> print(p)
<example.Pet named 'Luccy'>

導出類成員變量(public)

PYBIND11_MODULE(example, m)
{
    using pybind11::literals::operator""_a;
    py::class_<Pet>(m, "Pet")
            .def(py::init<const std::string &>(), "name"_a = "liuyang2")
            .def_readwrite("name", &Pet::name)
//            .def("setName", &Pet::setName)
//            .def("getName", &Pet::getName)
            .def("__repr__",
                 [](const Pet &a) {
                    return "<example.Pet named '"+ a.name +"'>";
                 });
}

直接使用成員變量

>>> import example
>>> p = example.Pet("ly")     
>>> p.name   
'ly'
>>> p.name = "sxl"
>>> p.name
'sxl'

導出成員變量(private)

PYBIND11_MODULE(example, m)
{
    using pybind11::literals::operator""_a;
    py::class_<Pet>(m, "Pet")
            .def(py::init<const std::string &>(), "name"_a = "liuyang2")
            .def_property("name", &Pet::getName, &Pet::setName)
//            .def_readwrite("name", &Pet::name)
//            .def("setName", &Pet::setName)
//            .def("getName", &Pet::getName)
            .def("__repr__",
                 [](const Pet &a) {
                    return "<example.Pet named '"+ a.getName() +"'>";
                 });
}

設置成員變量只讀屬性

PYBIND11_MODULE(example, m)
{
    using pybind11::literals::operator""_a;
    py::class_<Pet>(m, "Pet")
            .def(py::init<const std::string &>(), "name"_a = "liuyang2")
            .def_property("name", &Pet::getName, nullptr)
            //.def_readonly("name", &Pet::name)  // 與上面等效
}

添加類的動態屬性

以下代碼會報錯,Pet類中沒有 age 屬性

>>> p = example.Pet()
>>> p.name = 'Charly'  # OK, attribute defined in C++
>>> p.age = 2  # fail
AttributeError: 'Pet' object has no attribute 'age'

Pet類支持動態屬性,需要添加 py::dynamic_attr()

PYBIND11_MODULE(example, m)
{
    using pybind11::literals::operator""_a;
    py::class_<Pet>(m, "Pet", py::dynamic_attr())
            .def(py::init<const std::string &>(), "name"_a = "liuyang")
            .def_property("name", &Pet::getName, &Pet::setName);
}

現在可以正常運行

>>> p = example.Pet()
>>> p.name = 'Charly'  # OK, overwrite value in C++
>>> p.age = 2  # OK, dynamically add a new attribute
>>> p.__dict__  # just like a native Python class
{'age': 2}

類的繼承

#include <pybind11/pybind11.h>
#include <string>

namespace py = pybind11;

class Pet
{
public:
    Pet() = default;
    Pet(const std::string &name):name(name){}
    void setName(const std::string &name_)
    {
        name = name_;
    }
    const std::string &getName() const
    {
        return name;
    }
private:
    std::string name;
};

class Dog: public Pet
{
public:
    std::string bark()
    {
        return "woof!";
    }
};

PYBIND11_MODULE(example, m)
{
    using pybind11::literals::operator""_a;
    py::class_<Pet>(m, "Pet", py::dynamic_attr())
            .def(py::init<const std::string &>(), "name"_a = "liuyang")
            .def_property("name", &Pet::getName, &Pet::setName); 
			//.def_property("name", &Pet::getName, nullptr); // 只讀
    py::class_<Dog, Pet>(m, "Dog")
            .def(py::init<>())
            .def("bark", &Dog::bark, "Dog bark sound"); 
}

以下寫法等效

py::class_<Pet> pet(m, "Pet");
pet.def(py::init<const std::string &>())
   .def_readwrite("name", &Pet::name);

// Method 2: pass parent class_ object:
py::class_<Dog>(m, "Dog", pet /* <- specify Python parent type */)
    .def(py::init<>())
    .def("bark", &Dog::bark);

類指針添加動態屬性

添加以下代碼

m.def("pet_store", [](){return std::unique_ptr<Pet>(new Dog("Molly"));});

在python中測試報錯

>>> import example         
>>> p = example.pet_store()
>>> p.bark()               
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'example.Pet' object has no attribute 'bark'
>>> 

解決辦法是在Pet中添加一個任意的虛函數,比如

    virtual ~Pet() = default;
    // 或者
    // virtual void f(){}
>>> import example         
>>> p = example.pet_store()
>>> p.bark()
'woof1!'
>>> type(p)
<class 'example.Dog'>
>>> 

重載函數

#include <pybind11/pybind11.h>

namespace py = pybind11;

class Pet
{
public:
    Pet(std::string name): name(name)
    {}

    void set(const std::string &name_)
    {
        name = name_;
    }

    void set(int age_)
    {
        age = age_;
    }

    const std::string& getName()
    {
        return name;
    }

    int age;
private:
    std::string name;
};

PYBIND11_MODULE(example, m)
{
    py::class_<Pet>(m, "Pet")
            .def(py::init<const std::string &>())
            .def_property("name", &Pet::getName, nullptr)
            .def_readonly("age", &Pet::age)
            .def("set", (void (Pet::*)(int)) &Pet::set)
            .def("set", (void (Pet::*)(const std::string &)) &Pet::set);
}

或者可以這樣寫

PYBIND11_MODULE(example, m)
{
    py::class_<Pet>(m, "Pet")
            .def(py::init<const std::string &>())
            .def_property("name", &Pet::getName, nullptr)
            .def_readonly("age", &Pet::age)
            .def("set", py::overload_cast<int>(&Pet::set))
            .def("set", py::overload_cast<const std::string &>(&Pet::set));
//            .def("set", (void (Pet::*)(int)) &Pet::set)
//            .def("set", (void (Pet::*)(const std::string &)) &Pet::set);
}

const成員函數

struct Widget {
    int foo(int x, float y);
    int foo(int x, float y) const;
};

py::class_<Widget>(m, "Widget")
   .def("foo_mutable", py::overload_cast<int, float>(&Widget::foo))
   .def("foo_const",   py::overload_cast<int, float>(&Widget::foo, py::const_));

枚舉

#include <pybind11/pybind11.h>

namespace py = pybind11;

struct Pet
{
    enum Kind {
        Dog = 0,
        Cat
    };

    Pet(const std::string &name , Kind type) : name(name), type(type)
    {}

    std::string name;
    Kind type;
};

PYBIND11_MODULE(example, m)
{
    py::class_<Pet> pet(m, "Pet");
    pet.def(py::init<const std::string &, Pet::Kind>())
            .def_readwrite("name", &Pet::name)
            .def_readwrite("type", &Pet::type);

    py::enum_<Pet::Kind>(pet, "Kind")
            .value("Dog", Pet::Kind::Dog)
            .value("Cat", Pet::Kind::Cat)
            .export_values();
}
>>> import example                          
>>> p = example.Pet('Lucy', example.Pet.Cat)   
>>> p.type
Kind.Cat
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章