pybind11使用指南

0.参考资料

官方文档:pybind11文档

官方pdf:pybind11文档pdf

1.在python端调用函数 

//example.cpp
#include 
namespace py = pybind11;

int add(int i, int j) {
    return i + j;
}
//example是包的名称,add是包中函数的名称
//PYBIND11_MODULE是一个宏,m是py::module类型
PYBIND11_MODULE(example, m) {
    m.doc() = "pybind11 example plugin"; // optional module docstring

    m.def("add", &add, "A function which adds two numbers");
}
>>> import example
>>> example.add(1, 2)

可以通过help(example)查看库的帮助文档。

2.在python端为函数的指定参数赋值:

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

3.在python端为函数设置默认值:

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

4.在python端使用库中的变量

需要使用attr函数在模块中注册变量,内置类型在分配为属性时会自动转换,也可以使用py::cast函数显式转换。

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'

5.绑定c++模板函数

python是弱类型语言,而C++是强类型语言,如果要让python根据数据类型不同自动调用不同的C++函数,需要多次绑定。

//example.cpp
#include 
#include 
namespace py = pybind11;

template 
T square(T x) {
    return x * x;
}
PYBIND11_MODULE(example, m) {
    m.doc() = "pybind11 example plugin"; // optional module docstring

    m.def("square", &square);
    m.def("square", &square);
    m.def("square", &square);
}

6.绑定c++指针参数函数

//example.cpp
#include 

namespace py = pybind11;

void swap(int* a, int* b) {
    int temp;
    temp = *a;
    *a = *b;
    *b = temp;
}
PYBIND11_MODULE(example, m) {
    m.doc() = "pybind11 example plugin"; // optional module docstring

    m.def("swap", [](py::buffer a, py::buffer b) {\
        py::buffer_info a_info = a.request();
        py::buffer_info b_info = b.request();
        swap(static_cast(a_info.ptr), static_cast(b_info.ptr));
    });
}

7.在python端使用自定义结构体 

7.1绑定函数

//结构体定义
struct Pet {
    Pet(const string &name) : name(name) { }
    void setName(const string &name_) { name = name_; }
    const string& getName() const { return name; }
    //此处name变量是public的
    string name;
};

py::class_是模板类,py::init是注册构造函数 

//注册结构体
#include 
 
namespace py = pybind11;

PYBIND11_MODULE(example, m) {//这几行代码只使用了一个m,说明这几个函数都是绑在一起
        py::class_(m, "Pet")
        .def(py::init())
        .def("setName", &Pet::setName)
        .def("getName", &Pet::getName);
}
>>> import example
>>> p = example.Pet('Molly')
>>> print(p)

>>> p.getName()
u'Molly'
>>> p.setName('Charly')
>>> p.getName()
u'Charly'

静态成员函数可以使用class_::def_static()绑定。

7.2改进print信息

示例程序中print(p)是生成一个无用的数据结构摘要信息,可以绑定一个名为__repr__的函数增加摘要信息。

py::class_(m, "Pet")
    .def(py::init())
    .def("setName", &Pet::setName)
    .def("getName", &Pet::getName)
    .def("__repr__",
        [](const Pet &a) {
            return "";
        });

再执行print(p)得到的结果如下:

>>> print(p)

7.3绑定变量

可以使用class::def_readwrite()来直接注册变量,class::def_readonly注册const变量(常量)。

py::class_(m, "Pet")
    .def(py::init())
    .def_readwrite("name", &Pet::name)
    // ... remainder …
>>> p = example.Pet('Molly')
>>> p.name
u'Molly'
>>> p.name = 'Charly'
>>> p.name
u'Charly'

7.4绑定私有变量

私有变量是不能直接访问的,但是可以通过class_::def_property()(class_::defproperty_readonly()用于只读数据)绑定访问私有变量的setter和getter函数。

class Pet {
public:
    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;
};
py::class_(m, "Pet")
    .def(py::init())
    .def_property("name", &Pet::getName, &Pet::setName)
    // ... remainder ...

只读变量可以将第三个参数设为nullptr,只写变量可以将第二个参数设为nullptr。

class_::def_readwrite_static(),class_::def_readonly_static(),class_::def_property_static(),以及class_::def_property_readonly_static()用于绑定静态变量。

8.python端为类绑定动态属性

>>> class Pet:
...     name = 'Molly'
...
>>> p = Pet()
>>> p.name = 'Charly'  # 赋值
>>> p.age = 2  # 动态增加属性

C++类要支持动态属性,必须将py::dynamic_attr这个标记添加到py::class_构造函数中:

py::class_(m, "Pet", py::dynamic_attr())
    .def(py::init<>())
    .def_readwrite("name", &Pet::name);

9.python端实现继承

9.1非虚函数的继承

struct Pet {
    Pet(const string &name) : name(name) { }
    string name;
};
 
struct Dog : Pet {
    Dog(const string &name) : Pet(name) { }
    string bark() const { return "woof!"; }
};

有两种方式可以实现python端的继承关系,第一种将父类作为子类额外的模板参数。

py::class_(m, "Pet")
   .def(py::init())
   .def_readwrite("name", &Pet::name);
 
// Method 1: template parameter:
py::class_(m, "Dog")
    .def(py::init())
    .def("bark", &Dog::bark);

第二种先创建一个父类对象pet,在子类定义中传入pet,推荐第一种。

py::class_ pet(m, "Pet");
pet.def(py::init())
   .def_readwrite("name", &Pet::name);
 
// Method 2: pass parent class_ object:
py::class_(m, "Dog", pet /* <- specify Python parent type */)
    .def(py::init())
    .def("bark", &Dog::bark);
>>> p = example.Dog('Molly')
>>> p.name
u'Molly'
>>> p.bark()
u'woof!'

但是由于子类的bark是非虚函数,并且父类中没有任何虚函数,不能通过父类指针调用子类的bark函数。

// 在模块中添加这个函数用于测试
m.def("pet_store", []() { return unique_ptr(new Dog("Molly")); });
>>> p = example.pet_store()
>>> type(p)  # `Dog` instance behind `Pet` pointer
Pet          # no pointer downcasting for regular non-polymorphic types
>>> p.bark()
AttributeError: 'Pet' object has no attribute 'bark'

9.2虚函数的继承

struct Pet {
    virtual ~Pet() = default;
};
 
struct Dog : Pet {
    string bark() const { return "woof!"; }
};
 
// Same binding code
py::class_(m, "Pet");
py::class_(m, "Dog")
    .def(py::init<>())
    .def("bark", &Dog::bark);
 
// Again, return a base pointer to a derived instance
m.def("pet_store2", []() { return unique_ptr(new Dog); });
>>> p = example.pet_store2()
>>> type(p)
Dog  # automatically downcast
>>> p.bark()
u'woof!'

10.绑定重载函数

struct Pet {
    Pet(const string &name, int age) : name(name), age(age) { }
    //重载函数
    void set(int age_) { age = age_; }
    void set(const string &name_) { name = name_; }
 
    string name;
    int age;
};

如果直接绑定Pet::set将导致错误,因为编译器不知道是哪种方法。可以将它们转换为函数指针来消除歧义。多个函数绑定相同的名称会自动创建一系列重载函数。

py::class_(m, "Pet")
   .def(py::init())
   .def("set", (void (Pet::*)(int)) &Pet::set, "Set the pet's age")
   .def("set", (void (Pet::*)(const std::string &)) &Pet::set, "Set the pet's name");

11.返回值策略

Python和C ++使用根本不同的方式来管理对象的内存和生命周期,当绑定返回较复杂类型的函数时,可能会导致问题。仅通过查看类型信息,尚不清楚Python是否应该掌管返回的值并负责释放其资源,还是在C ++端进行处理。所以pybind11提供了一些返回值参数,可以将其传递给module::def()and class_::def()函数,默认策略是 return_value_policy::automatic。

m.def("get_data",&get_data,py::return_value_policy::reference);
                   返回值策略                                                描述
return_value_policy::take_ownership 管理现有对象(但不创建新副本)并获得所有权。当对象的引用计数达到零时,Python将调用析构函数和delete运算符。当C++端执行相同操作或未动态分配数据时,就会发生未定义的行为。
return_value_policy::copy 创建返回对象的新副本,该副本将归Python所有。此策略相对比较安全,因为这两个实例的生命周期是分离的。
return_value_policy::move 使用std::move将返回值内容移动到将由Python拥有的一个新的实例中。此策略相对比较安全,因为两个实例(移动源和目标)的生存期已分离。
return_value_policy::reference 引用现有对象,但不拥有所有权。C ++端负责管理对象的生存期,并在不再使用该对象时对其进行分配。警告:当C ++端删除仍由Python引用和使用的对象时,将会发生未定义的行为。
return_value_policy::reference_internal 指示返回值的生存期与父对象的生存期相关联,该父对象即被调用方法或属性的隐式this或自self变量。在内部,此策略的工作原理与之类似,return_value_policy::reference但是另外还应用了 调用策略(在下一节中进行描述),只要返回值被Python引用,该策略就 可以防止父对象被垃圾回收。这是通过创建属性获取默认的策略,等等。keep_alive<0, 1> def_propertydef_readwrite
return_value_policy::automatic 默认策略。return_value_policy::take_ownership当返回值是一个指针时,此策略将回退到该策略 。否则,它分别将return_value_policy::move或 return_value_policy::copy用于rvalue和lvalue引用。有关所有这些不同策略的作用的说明,请参见上文。
return_value_policy::automatic_reference 如上所述,但是return_value_policy::reference当返回值是指针时使用策略。当从C ++代码手动调用Python函数时(即通过handle :: operator()),这是函数参数的默认转换策略。您可能不需要使用它。

你可能感兴趣的:(工具,C++学习,Python学习,python,c++,pybind11)