如何在Python中使用pybind11绑定隐式构造函数/转换?
我正在尝试在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 个回答
你引用的文档提到的是 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 的list
、set
、dict
数据结构之间的转换会自动启用。而std::pair<>
和std::tuple<>
则只需要包含核心的pybind11/pybind11.h
头文件就能直接使用。
这里有一个可以运行的示例: https://godbolt.org/z/WrPrb8cM8