pybind11中文资料(第五章 面向对象的代码)
在此我要特别感谢pybind11项目创立者 Wenzel Jakob以及众多项目参与者
5面向对象的代码
5.1创建自定义类型的绑定
现在来看一个更复杂的例子,这个例子为名为Pet的自定义数据结构创建绑定。
Pet定义如下:
struct Pet {Pet(const std::string &name) : name(name) { }void setName(const std::string &name_) { name = name_; }const std::string &getName() const { return name; }std::string name;
};
Pet的绑定如下:
#include <pybind11/pybind11.h>namespace py = pybind11;PYBIND11_MODULE(example, m) {py::class_<Pet>(m, "Pet").def(py::init<const std::string &>()).def("setName", &Pet::setName).def("getName", &Pet::getName);
}
class_为C ++类或结构体类型的数据结构创建绑定。init()将构造函数的参数类型作为模板参数并包装相应的构造函数(详细信息请参阅“ 自定义构造函数”部分)。
示例演示如下:
% python
>>> import example
>>> p = example.Pet('Molly')
>>> print(p)
<example.Pet object at 0x10cd98060>
>>> p.getName()
u'Molly'
>>> p.setName('Charly')
>>> p.getName()
u'Charly'
See also: 静态成员函数可以使用class_::def_static()同样也能绑定。
5.2关键字和默认参数
可以使用前一章中讨论的语法来指定关键字和默认参数。详细信息,请参阅关键字参数和默认参数部分。
5.3绑定lambda函数
请注意上面示例中print(p)是如何生成一个无用的数据结构摘要信息:
>>> print(p)
<example.Pet object at 0x10cd98060>
为了解决这个问题,我们可以绑定一个名为__repr__的函数,该函数返回人可读的摘要。不幸的是,Pet数据结构中没有相应的功能,如果我们不必更改Pet的机构将会更好。这些可以通过绑定Lambda函数达到:
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 + "'>";});
pybind11支持无状态[1]和有状态lambda闭包。通过上述更改,相同的Python代码生成以下输出:
>>> print(p)
<example.Pet named 'Molly'>
5.4实例和静态字段
我们也可以使用class::def_readwrite()来直接暴露name字段。类似的class::def_readonly适用于const字段。
py::class_<Pet>(m, "Pet").def(py::init<const std::string &>()).def_readwrite("name", &Pet::name)// ... remainder …
>>> p = example.Pet('Molly')
>>> p.name
u'Molly'
>>> p.name = 'Charly'
>>> p.name
u'Charly'
现在假设这Pet::name 是一个私有的内部变量,只能通过setter和getters访问。
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_<Pet>(m, "Pet").def(py::init<const std::string &>()).def_property("name", &Pet::getName, &Pet::setName)// ... remainder ...
只写属性可以通过传递nullptr给读取函数作为输入来定义。
5.5动态属性
>>> class Pet:
... name = 'Molly'
...
>>> p = Pet()
>>> p.name = 'Charly' # overwrite existing
>>> p.age = 2 # dynamically add a new attribute
默认情况下,从C ++导出的类不支持此操作,唯一可写的是使用class_::def_readwrite() 或者 class_::def_property()显式定义的属性。
py::class_<Pet>(m, "Pet").def(py::init<>()).def_readwrite("name", &Pet::name);
>>> p = example.Pet()
>>> p.name = 'Charly' # OK, attribute defined in C++
>>> p.age = 2 # fail
AttributeError: 'Pet' object has no attribute 'age'
C++类要启用动态属性,必须将py::dynamic_attr这个标记添加到py::class_构造函数中:
py::class_<Pet>(m, "Pet", py::dynamic_attr()).def(py::init<>()).def_readwrite("name", &Pet::name);
>>> 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}
5.6继承和自动转换
struct Pet {Pet(const std::string &name) : name(name) { }std::string name;
};struct Dog : Pet {Dog(const std::string &name) : Pet(name) { }std::string bark() const { return "woof!"; }
};
有两种不同的方式来显示出与pybind11的层次关系:第一种是指定C++基类作为class_的额外模板参数:
py::class_<Pet>(m, "Pet").def(py::init<const std::string &>()).def_readwrite("name", &Pet::name);// Method 1: template parameter:
py::class_<Dog, Pet /* <- specify C++ parent type */>(m, "Dog").def(py::init<const std::string &>()).def("bark", &Dog::bark);
第二种,我们也可以为之前绑定的Pet class_对象指定名称,并在绑定Dog类时引用它
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<const std::string &>()).def("bark", &Dog::bark);
功能方面,两种方法都是等价的。实例将公开两种类型的字段和方法:
>>> p = example.Dog('Molly')
>>> p.name
u'Molly'
>>> p.bark()
u'woof!'
上面定义的C++类是具有继承关系的常规非多态类型。这反映在Python中:
// Return a base pointer to a derived instance
m.def("pet_store", []() { return std::unique_ptr<Pet>(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'
该函数返回了一个Dog实例,但因为它是基类指针指向的派生类型,所以Python只能看到Pet。在C++中,如果一个类型包含至少一个虚函数,那么被认为是多态的,pybind11会自动识别它:
struct PolymorphicPet {virtual ~PolymorphicPet() = default;
};struct PolymorphicDog : PolymorphicPet {std::string bark() const { return "woof!"; }
};// Same binding code
py::class_<PolymorphicPet>(m, "PolymorphicPet");
py::class_<PolymorphicDog, PolymorphicPet>(m, "PolymorphicDog").def(py::init<>()).def("bark", &PolymorphicDog::bark);// Again, return a base pointer to a derived instance
m.def("pet_store2", []() { return std::unique_ptr<PolymorphicPet>(new PolymorphicDog); });
>>> p = example.pet_store2()
>>> type(p)
PolymorphicDog # automatically downcast
>>> p.bark()
u'woof!'
给定指向多态基类的指针,pybind11执行自动转换为派生类型。请注意,这超出了C++中的通常情况:不只是访问基类的虚函数,还可以获得具体的派生类,这包括一些基类未声明过的函数和属性。
See also: 有关多态的更多信息,请参阅在Python中覆盖虚函数。
5.7重载方法
有时会有几个具有相同名称的C++方法,这些方法采用不同的输入参数类型:
struct Pet {Pet(const std::string &name, int age) : name(name), age(age) { }void set(int age_) { age = age_; }void set(const std::string &name_) { name = name_; }std::string name;int age;
};
尝试绑定Pet::set将导致错误,因为编译器不知道应该指定哪种方法。我们可以通过将它们转换为函数指针来消除歧义。将多个函数绑定到相同的名称会自动创建一系列函数重载。
py::class_<Pet>(m, "Pet").def(py::init<const std::string &, int>()).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");
>>> help(example.Pet)class Pet(__builtin__.object)| Methods defined here:|| __init__(...)| Signature : (Pet, str, int) -> NoneType|| set(...)| 1. Signature : (Pet, int) -> NoneType|| Set the pet's age|| 2. Signature : (Pet, str) -> NoneType|| Set the pet's name
如果你有一个兼容C++ 14的编译器[2],你可以使用另一种语法来转换重载函数:
py::class_<Pet>(m, "Pet").def("set", py::overload_cast<int>(&Pet::set), "Set the pet's age").def("set", py::overload_cast<const std::string &>(&Pet::set), "Set the pet's name");
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_));
[2]支持-std=c++14
标志或Visual Studio 2015 Update 2及更高版本的编译器。
Note: 要定义多个重载的构造函数,只需使用.def(py::init<...>())语法一个一个地声明。用于指定关键字和默认参数的机制也有效。
Note: 要定义多个重载的构造函数,只需使用.def(py::init<...>())语法一个一个地声明。用于指定关键字和默认参数的机制也有效。
5.8枚举和内部类型
struct Pet {enum Kind {Dog = 0,Cat};Pet(const std::string &name, Kind type) : name(name), type(type) { }std::string name;Kind type;
};
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();
>>> p = Pet('Lucy', Pet.Cat)
>>> p.type
Kind.Cat
>>> int(p.type)
1L
>>> Pet.Kind.__members__
{'Dog': Kind.Dog, 'Cat': Kind.Cat}
Note: 也可以使用str(enum),但是这达到了不同的目的。以下显示了这两种方法的不同之处。
>>> p = Pet( "Lucy", Pet.Cat )
>>> pet_type = p.type
>>> pet_type
Pet.Cat
>>> str(pet_type)
'Pet.Cat'
>>> pet_type.name
'Cat'
Note: 当为enum_构造函数指定py::arithmetic()标签,pybind11会创建一个枚举,该枚举支持基本算术和位级操作,如比较、and、or、xor、否等。
py::enum_<Pet::Kind>(pet, "Kind", py::arithmetic())...
pybind11中文资料(第五章 面向对象的代码)相关推荐
- 第十五章 面向对象程序设计
第十五章 面向对象程序设计 OOP:概述 面向对象程序设计(object-oriented programming)的核心思想是数据抽象.继承和动态绑定. 继承(inheritance): 通过继承联 ...
- Java第五章——面向对象(二)
第五章面向对象(中) 1.访问控制修饰符 2.继承 3.组合 4.重载 5.覆盖(覆写) 重点: 封装(java访问控制权限) 继承(定义.子类访问控制权限) 类的组合的实现(与继承的区别) 多态(重 ...
- 第五章 函数和代码复用
第五章 函数和代码复用 5.1 函数的基本使用 5.1.1 函数的定义 定义:函数是一段具有特定功能的.可重用的语句组,用函数名来表示并通过函数名进行功能调用. 使用函数的目的:降低编程难度和代码重用 ...
- 第五章 面向对象(上)
Java是面向对象的程序设计语言,Java语言提供了定义类.成员变量.方法等最基础的功能,类可被认为是一种自定义的数据类型,可以使用类来定义变量,所有使用类定义的变量都是引用变量,它们将引用到类的对象 ...
- oracle 10g 新特性中文笔记(第五章)
第五章 管理体系结构 目标 完成本课,你应该能: 描述自动工作负载资料库(AWR)的各种不同的组件 使用活动会话历史 定义AWR快照基线 对服务端的警告订阅应用程序 解释自动任务特征 描述顾问咨询框架 ...
- 第二十五章 面向对象------封装、内置函数、反射、动态导入
1.封装 什么是封装? 1.对外部隐藏内部的属性,以及实现细节,给外部提供使用的接口 注意:封装有隐藏的意思,但不是单纯的隐藏 学习封装的目的:就是为了能够限制外界对内部数据的访问 python中属性 ...
- 第五章 ---- 面向对象(二)
面向对象二 2.三大特征 2.1.继承性 2.1.1.方法重写 2.1.2.子类实例化过程 2.2.多态性 3.关键字 3.1.super 4.常用类说明 String String概述 字面量方式 ...
- 第五章面向对象编程设计与开发
5.1 什么是面向对象的程序设计 面向过程的程序设计 优点:复杂的问题流程化,进而简单化 缺点:一套流水线或者流程就是用来解决一个问题,扩展性极差. 应用场景:面向过程的程序设计思想一般用于那些功能一 ...
- c++primer(第五版) 第十五章 面向对象程序设计习题答案
纯原创 转载请注明出处:http://blog.csdn.net/axuan_k 略过书上有现成代码的题目 15.1 15.2 15.1 虚成员是基类中的概念 基类中的某些函数希望它的派生类能 ...
最新文章
- 透过源码详解Spring Security 初始化流程
- Python date,datetime,time等相关操作总结
- vue2.0 唤起百度地图app_如何标注百度地图?
- 7大最重要的管理方法
- Codeforces Round #661 (Div. 3)
- 从Memcache转战Redis,聊聊缓存使用填过的“坑”
- 《spring-boot学习》-09-spring boot+mybatis多数据源
- java merge_java – OptimisticLockException当使用JPA merge()
- C# Task 循环任务_聊聊 JavaScript 的并发、异步和事件循环
- 敏捷开发团队管理系列之五:大型研发团队的切分(刚参加3.17 MDP团队管理场次的读者请看)...
- zrender源码分析4--初始化Painter绘图模块2
- 利用Python使图片完美去除水印,我想试试马赛克的效果∧v∧
- 12306 抢票助手 Java
- 标识符——C语言基础知识
- 〖Python零基础入门篇④〗- Pycharm插件安装与常用的插件
- 【论文复现】CBAM(2018)
- css 文本移除及省略号位置
- 基于树莓派的智能家居控制系统设计论文参考
- 表格中insertBefore方法的使用(The node before which the new node is to be inserted is not a child of this no)
- 用pycharm进行python爬虫的步骤_使用Pycharm写一个网络爬虫