本文主要记录官方文档中 CLASSES 一章的学习笔记。
对于C++ 类的Python绑定,在前面的学习中已经有所涉及了,详见:pybind11学习 | 面向对象编程 。本文主要是记录一些更加深入的知识。在本文中只涉及了一些我感兴趣的部分,其他部分详见官方文档CLASSES 一章。
[TOC]
1 在Python中重载虚函数 一个含有虚函数的C++类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Animal {public : virtual ~Animal () { } virtual std::string go (int n_times) = 0 ; };class Dog : public Animal {public : std::string go (int n_times) override { std::string result; for (int i=0 ; i<n_times; ++i) result += "woof! " ; return result; } };std::string call_go (Animal *animal) { return animal->go (3 ); }
我们在Python中自定义一个继承自Animal
的新类,并想要重载基类中的虚函数。此时,直接class_::def
对Animal
类的虚函数进行绑定是不行的,需要在C++中再定义一个新的PyAnimal类(继承自Animal类)作为辅助跳板 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class PyAnimal : public Animal {public : using Animal::Animal; std::string go (int n_times) override { PYBIND11_OVERRIDE_PURE ( std::string, Animal, go, n_times ); } };
上面代码中,定义纯虚函数时需要使用PYBIND11_OVERRIDE_PURE
宏,而有默认实现的虚函数则使用PYBIND11_OVERRIDE
。PYBIND11_OVERRIDE_PURE_NAME
和PYBIND11_OVERRIDE_NAME
宏的功能类似,主要用于C函数名和Python函数名不一致的时候。pybind11绑定代码如下:
1 2 3 4 5 6 7 8 9 10 PYBIND11_MODULE (example, m) { py::class_<Animal, PyAnimal >(m, "Animal" ) .def (py::init<>()) .def ("go" , &Animal::go); py::class_<Dog, Animal>(m, "Dog" ) .def (py::init<>()); m.def ("call_go" , &call_go); }
pybind11通过向class_
指定额外的模板参数PyAnimal,让我们可以在Python中继承Animal类(即重载C++类中的为虚函数的构造函数)。Python中测试如下:
1 2 3 4 5 6 7 8 9 from example import * d = Dog() call_go(d) class Cat (Animal ): def go (self, n_times ): return "meow! " * n_times c = Cat() call_go(c)
2 自定义构造函数 若C++类中没有构造函数,我们可以显式地将自定义函数作为类的构造函数绑定到Python类的__init__
方法上。pybind11通过调用.def(py::init(...))
,将对应的函数(函数需要返回一个新实例)作为参数传入py::init()
实现。py::init()
也可以传入返回新实例原始指针或持有者的匿名函数。
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 class Example {private : Example (int ); public : static Example create (int a) { return Example (a); } Example (double ); Example (int , int ); Example (std::string); }; py::class_<Example>(m, "Example" ) .def (py::init (&Example::create)) .def (py::init ([](std::string arg) { return std::unique_ptr<Example>(new Example (arg)); })) .def (py::init ([](int a, int b) { return new Example (a, b); })) .def (py::init<double >()) ;
pybind11
使用C++11的大括号初始化来隐式调用目标类的构造函数。
1 2 3 4 5 6 7 struct Aggregate { int a; std::string b; }; py::class_<Aggregate>(m, "Aggregate" ) .def (py::init<int , const std::string &>());
3 隐式转换 假设有A和B两个类,A可以直接转换为B。
1 2 3 4 5 6 7 8 9 10 py::class_<A>(m, "A" ) py::class_<B>(m, "B" ) .def (py::init<A>()) m.def ("func" , [](const B &) { } );
如果想func函数传入A类型的参数a,Pyhton侧需要这样写func(B(a))
,而C++侧则可以直接使用func(a)
,自动将A类型转换为B类型。
这种情形下(B有一个接受A类型参数的构造函数),我们可以使用如下声明来让Python侧也支持类似的隐式转换:
1 py::implicitly_convertible<A, B>();
4 重载操作符 假设有这样一个类Vector2
,它通过重载操作符实现了向量加法和标量乘法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Vector2 {public : Vector2 (float x, float y) : x (x), y (y) { } Vector2 operator +(const Vector2 &v) const { return Vector2 (x + v.x, y + v.y); } Vector2 operator *(float value) const { return Vector2 (x * value, y * value); } Vector2& operator +=(const Vector2 &v) { x += v.x; y += v.y; return *this ; } Vector2& operator *=(float v) { x *= v; y *= v; return *this ; } friend Vector2 operator *(float f, const Vector2 &v) { return Vector2 (f * v.x, f * v.y); } std::string toString () const { return "[" + std::to_string (x) + ", " + std::to_string (y) + "]" ; }private : float x, y; };
操作符绑定代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include <pybind11/operators.h> PYBIND11_MODULE (example, m) { py::class_<Vector2>(m, "Vector2" ) .def (py::init<float , float >()) .def (py::self + py::self) .def (py::self += py::self) .def (py::self *= float ()) .def (float () * py::self) .def (py::self * float ()) .def (-py::self) .def ("__repr__" , &Vector2::toString); }
参考 官方文档:pybind11 documentation
官方文档中文翻译:pybind11-Chinese-docs: pybind11中文文档