pybind11学习 | 函数的绑定
admin
2024-04-29 06:11:00
0

本文主要记录官方文档中 FUNCTIONS 一章的学习笔记。

对于C++ 函数的Python绑定,在前面的学习中已经有所涉及了,详见:pybind11学习 | 迈出第一步。本文主要是记录一些更加深入的知识。

文章目录

    • 1 返回值策略
    • 2 调用策略
      • 2.1 keep alive
      • 2.2 call guard
    • 3 默认参数
    • 4 Keyword-only参数
    • 5 Positional-only参数
    • 6 Non-converting参数
    • 7 允许/禁止None参数

1 返回值策略

Python和C++在内存管理和对象生命周期管理上存在本质的区别。为此,pybind11提供了一些返回值策略来确定由哪方管理资源。pybind11在绑定C++函数时,一个有7个返回值策略,都在py::return_value_policy(py为pybind11的别名)枚举类型中。这些策略通过model_::def()(模块函数)和class_::def()(类成员方法)来指定,默认策略为return_value_policy::automatic

返回值策略描述
return_value_policy::take_ownership引用现有对象(不创建一个新对象),并获取所有权。在引用计数为0时,Pyhton将调用析构函数和delete操作销毁对象。
return_value_policy::copy拷贝返回值,这样Python将拥有拷贝的对象。该策略相对来说比较安全,因为两个实例的生命周期是分离的。
return_value_policy::move使用std::move来移动返回值的内容到新实例,新实例的所有权在Python。该策略相对来说比较安全,因为两个实例的生命周期是分离的。
return_value_policy::reference引用现有对象,但不拥有所有权。C++侧负责该对象的生命周期管理,并在对象不再被使用时负责析构它。注意:当Python侧还在使用引用的对象时,C++侧删除对象将导致未定义行为。
return_value_policy::reference_internal返回值的生命周期与父对象的生命周期相绑定,即被调用函数或属性的thisself对象。这种策略与reference策略类似,但附加了keep_alive<0, 1>调用策略保证返回值还被Python引用时,其父对象就不会被垃圾回收掉。这是由def_propertydef_readwrite创建的属性getter方法的默认返回值策略。
return_value_policy::automatic当返回值是指针时,该策略使用return_value_policy::take_ownership。反之对左值和右值引用使用return_value_policy::copy
return_value_policy::automatic_reference和上面一样,但是当返回值是指针时,使用return_value_policy::reference策略。这是在C++代码手动调用Python函数和使用pybind11/stl.h中的casters时的默认转换策略。你可能不需要显式地使用该策略。
  • 代码使用无效的返回值策略将导致未初始化内存或多次释放数据结构,这将导致难以调试的、不确定的问题和段错误。
  • 如果函数返回值为智能指针,可以不必指定返回值策略。

2 调用策略

通过指定调用策略可以表明参数间的依赖关系,确保函数调用的稳定性

2.1 keep alive

当一个C++容器对象包含另一个C++对象时,我们需要使用该策略。keep_alive表明在索引Nurse被回收前,索引Patient应该被keep alive。0表示返回值,1及以上表示参数索引。1表示隐含的参数this指针,而常规参数索引从2开始。当Nurse的值在运行前被检测到为None时,调用策略将什么都不做。

通过该策略,我们可以实现将被包含对象的声明周期绑定到包含对象上,示例如下:

py::class_(m, "List").def("append", &List::append, py::keep_alive<1, 2>());py::class_(m, "Nurse").def(py::init(), py::keep_alive<1, 2>());

2.2 call guard

call_guard策略允许任意T类型的scope guard应用于整个函数调用。示例如下:

m.def("foo", foo, py::call_guard());

call_guard类模板源码声明如下:

/** \rstA call policy which places one or more guard variables (``Ts...``) around the function call. \endrst */
template 
struct call_guard;template <>
struct call_guard<> {using type = detail::void_type;
};template 
struct call_guard {static_assert(std::is_default_constructible::value,"The guard type must be default constructible");using type = T;
};template 
struct call_guard {struct type {T guard{}; // Compose multiple guard types with left-to-right default-constructor ordertypename call_guard::type next{};};
};

3 默认参数

默认参数在声明时就已经被转换为Python对象了。如果默认参数为自定义类型,需要保证在class_::def中声明默认参数前,先将该自定义类型进行绑定。

py::class_("SomeType")//...py::class_("MyClass").def("myFunction", py::arg("arg") = SomeType(123));

使用py::arg_v给默认参数手动添加方便阅读的注释。

py::class_("MyClass").def("myFunction", py::arg_v("arg", SomeType(123), "SomeType(123)"));

使用空指针作为默认参数:

py::class_("MyClass").def("myFunction", py::arg("arg") = static_cast(nullptr));

4 Keyword-only参数

Python3引入Keyword-only参数语法。用法参见《Fluent Python》笔记 | 函数对象和装饰器中仅限关键字参数部分。

def f(a, *, b):  # a can be positional or via keyword; b must be via keywordpassf(a=1, b=2)  # good
f(b=2, a=1)  # good
f(1, b=2)  # good
f(1, 2)  # TypeError: f() takes 1 positional argument but 2 were given

pybind11提供了py::kw_only对象来实现相同的功能:

m.def("f", [](int a, int b) { /* ... */ },py::arg("a"), py::kw_only(), py::arg("b"));

注,该特性不能与py::args一起使用。

5 Positional-only参数

python3.8引入了Positional-only参数语法。即只能按位置赋值,不能通过关键字赋值。

pybind11通过py::pos_only()来提供相同的功能:

m.def("f", [](int a, int b) { /* ... */ },py::arg("a"), py::pos_only(), py::arg("b"));

6 Non-converting参数

当对Non-converting参数进行转换(包含隐式转换)时,代码会抛出错误。

Non-converting参数通过py::arg来调用.noconvert()方法指定。

m.def("floats_only", [](double f) { return 0.5 * f; }, py::arg("f").noconvert());	

7 允许/禁止None参数

使用py::arg对象的.none(bool)方法来显式地允许或禁止在Python中传入的该参数为None。在不显式指定的情况下,默认支持传递None

相关内容

热门资讯

独家 | 美团快乐猴签约10店... 交流快乐猴,实名添加微信lihua759321进群有市场人士告诉《商业观察家》,美团做的硬折扣线下超...
读懂IPO|红星冷链的业绩“冷... 本文来源:时代商业研究院 作者:彭元重 来源|时代商业研究院作者|彭元重编辑|郑琳近年来,随着消费端...
租客又找麻烦了... 租客又找... 图: charlotte.ager 我最近在看一位澳洲华人写的博客,写得很好,不知不觉我把他七八年的...
罗马仕最新声明:召回服务长期有... 7月15日,@ROMOSS罗马仕发布最新声明: 亲爱的朋友: 近日,我们关注到网络上关于罗马仕的大量...
英伟达开盘涨出大半个AMD!黄... 受H20芯片即将恢复对华销售利好提振,当地时间7月15日美股开盘后,英伟达股价大涨超过3%,市值增加...
55亿良品铺子「卖身」,零食江... 订阅 快刀财经 ▲ 做您的私人商学院包装和营销造就的高端故事已成往事,效率与价格正成为新赛点。作者 ...
英伟达、AMD股价开盘大涨,G... 美股周二开盘,AMD股价涨超8%,英伟达也涨超4%。消息面上,英伟达今日宣布将恢复AI芯片H20在中...
读懂IPO|剑桥科技业绩回弹后... 本文来源:时代商业研究院 作者:雷小艳 来源|时代商业研究院作者|雷小艳编辑|郑琳7月15日,剑桥科...
金价上涨叠加产能扩张,黄金矿企... 尽管黄金市场近期跌宕起伏,但黄金矿企在今年上半年已是赚得盆满钵满。国内黄金企业近日密集发布2025年...