拆分 pybind11 模块和自动类型转换的问题

Posted

技术标签:

【中文标题】拆分 pybind11 模块和自动类型转换的问题【英文标题】:Splitting up pybind11 modules and issues with automatic type conversion 【发布时间】:2018-08-14 03:25:47 【问题描述】:

我有一组用 C++ 编写并使用 pybind11 导出到 Python 的模块。所有这些模块都应该能够独立使用,但它们使用一组在实用程序库中定义的通用自定义类型。

在每个模块中都有类似于下面的代码。 Color.hpp 标头定义了实用程序库中使用的类型。

#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <string>
#include "Color.hpp"

std::vector<Color> buncha_colors(int n, std::string &color) 
    std::vector<Color> out;
    for (;n-- > 0;) 
        out.push_back(Color(color));
    
    return out;


PYBIND11_MODULE(pb11_example_module, m) 
    m.def("buncha_colors", &buncha_colors);

当然,这是行不通的。 Pybind 不知道如何对 Color 对象进行类型转换。答案(或希望不是)是将Color 类定义为模块的一部分。之后,pybind 能够进行自动类型转换。

#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <string>
#include "Colors.hpp"

std::vector<Color> buncha_colors(int n, std::string &color) 
    std::vector<Color> out;
    for (;n-- > 0;) 
        out.push_back(Color(color));
    
    return out;


PYBIND11_MODULE(pb11_example_module, m) 
    pybind11::class_<Color>(m, "Color")
        .def(pybind11::init<std::string&>())
        .def("name", &Color::name);
    m.def("buncha_colors", &buncha_colors);

理想情况下,我希望将所有这些自定义实用程序类型和相关函数与使用它们的所有模块分开保存在一个单独的模块中。但是我需要在使用它的每个模块中定义类型转换,或者以其他方式引用它。我该怎么做?我不想要pb11_example_module.Colorutils.Color 等等。我不知道它们的兼容性,而且这似乎是错误的方式。

【问题讨论】:

【参考方案1】:

这开始是一个编辑,但结果是我的答案。

所以这很有趣。使用第一个示例,其中 Color 未在 pybind 模块中导出...

$ python
>>> import pb11_example_module
>>> pb11_example_module.buncha_colors(10, "red")[0].name()
TypeError: Unable to convert function return value to a Python type! The signature was
    (arg0: int, arg1: str) -> List[Color]
>>> import utils  # defines Color
>>> pb11_example_module.buncha_colors(10, "red")[0].name()
'red'

在导入示例模块之前导入实用程序库也可以。将类名 "Color" 更改为其他名称也不会破坏用法,因此它必须使用类型签名从其他模块获取类型转换。

只要在使用前为该 C++ 类型定义了类型转换,自动类型转换就可以工作。 pybind 用于 Python 的类型转换实用程序是全局的,并在运行时查找。您可以阅读有关它的所有信息here。上述在使用前随时为自定义类型加载类型转换的解决方案是受支持的惯用解决方案。

【讨论】:

只是跟进,我认为更惯用的解决方案(在您的绑定中)是确保您已使用 py::module::import("my_package.my_depdendency") 导入模块。最终,b/c 您正在与您提到的全局注册表进行绑定,导入模块有副作用 - 通过 RTTI 注册类型。如果您在绑定中导入模块依赖项,那么您就可以开始了。这是一个示例 PR + 测试,如果忘记(对于我们的代码)会很快失败:github.com/RobotLocomotion/drake/pull/14072/files @EricCousineau 很棒的提示!

以上是关于拆分 pybind11 模块和自动类型转换的问题的主要内容,如果未能解决你的问题,请参考以下文章

Pybind11:外部类型作为返回值

如何使用 pybind11 将 python 函数转换为 std::function

pybind11 以某种方式减慢了 c++ 函数

使用pybind11开发python扩展库

简单的 pybind11 模块失败,没有命名模块

PyBind11:boost::multiprecision::cpp_int 到 Python