如何在Python中使用pybind11绑定隐式构造函数/转换?

0 投票
1 回答
58 浏览
提问于 2025-04-13 19:32

我正在尝试在Python中使用 pybind11 模仿一个简单结构体的隐式转换/构造。

struct MyType {
    double a;
    double b;
};

void func(size_t foo, const MyType& bar) {
    // ...
}

// ...


PYBIND11_MODULE(pymylib, module) {
    // ...
    py::class_< MyType >(module, "MyType")
        .def(py::init< double, double >(), "a"_a, "b"_a)
        .def(py::init([](const std::array< double, 2 >& ab){ return MyType({ab[0], ab[1]}); }), "ab"_a = std::array< double, 2>{ 1.0, 0.25 })   // not implicit
        .def_readwrite("a", &MyType::a)
        .def_readwrite("b", &MyType::b);

    py::implicitly_convertible< std::array< double, 2 >, MyType >();   // conversion is not implicit

    module.def("func", &func, "foo"_a, "bar"_a);
}

在C++中,可以使用大括号初始化 {},而不需要显式调用 MyType 的构造函数(这样语法更简单)。但是,当我把 MyType 作为参数传给 func 时,依然无法实现从列表对象的隐式转换:

obj = module.MyType(8.7, 5.6)

module.func(47, obj)                        # works
module.func(47, module.MyType([4.1, 7.8]))  # works
module.func(47, [4.1, 7.8])                 # does not work ('incompatible function arguments')

根据我从 pybind11 的文档 理解到的,构造函数声明 py::init< double, double >() 应该可以隐式绑定大括号初始化,但它并没有按我预期的那样工作:

TypeError: func(): incompatible function arguments. The following argument types are supported:
    1. module.func(a: float, b: module.MyType)

我尝试添加一个接受列表/数组的自定义构造函数,但它仍然没有被隐式调用。

由于这个结构体很简单,显式调用构造函数似乎没有必要,特别是在定义在Python子模块或嵌套在其他类中的情况下;如果可能的话,我该如何在Python中实现这种行为呢?

1 个回答

2

你引用的文档提到的是 Python 的初始化器如何调用 C++ 的构造函数。根据我所了解,它没有提到从 list 的隐式转换。正如你所怀疑的,你需要注册一个特殊的 py::init() 来处理这个问题。

关于你的特殊 py::init() 为什么不工作的原因,我猜可能是你缺少了 #include <pybind11/stl.h> 这个头文件。你可以查看这个链接了解更多信息:https://pybind11.readthedocs.io/en/stable/advanced/cast/stl.html

当你包含额外的头文件 pybind11/stl.h 时,std::vector<>std::deque<>std::list<>std::array<>std::valarray<>std::set<>std::unordered_set<> 以及 std::map<>std::unordered_map<> 和 Python 的 listsetdict 数据结构之间的转换会自动启用。而 std::pair<>std::tuple<> 则只需要包含核心的 pybind11/pybind11.h 头文件就能直接使用。

这里有一个可以运行的示例: https://godbolt.org/z/WrPrb8cM8

撰写回答