Pybind11访问嵌套不透明向量报错,如何重载索引运算符?
解决pybind11二维不透明vector索引返回类型不匹配问题
这不是pybind11的bug,而是嵌套不透明容器绑定场景下需要手动补全的一个细节——默认的bind_vector生成的二维容器类,不会自动把内层的std::vector<Example>转换成你已经定义好的ExampleVector类型,所以Python无法识别索引操作的返回值,才会抛出那个类型转换错误。
问题根源
你已经用PYBIND11_MAKE_OPAQUE标记了一维和二维vector,也分别绑定了ExampleVector和Example2DVector,但pybind11默认的bind_vector为二维容器生成的__getitem__方法,返回的是原始的std::vector<Example>不透明类型,而不是你已经绑定好的ExampleVectorPython类,所以Python无法完成类型转换,报错也就不可避免了。
解决方案:手动重载__getitem__方法
我们需要在绑定Example2DVector的时候,手动重载它的索引运算符,明确将返回的内层vector包装成ExampleVector类型。修改后的C++绑定代码如下:
#include <pybind11/pybind11.h> #include <pybind11/stl.h> #include <pybind11/stl_bind.h> #include <vector> class Example {}; PYBIND11_MAKE_OPAQUE(std::vector<Example>); PYBIND11_MAKE_OPAQUE(std::vector<std::vector<Example>>); PYBIND11_MODULE(ExModule, m) { // 绑定基础Example类 pybind11::class_<Example>(m, "Example").def(pybind11::init<>()); // 绑定一维vector,生成ExampleVector类 auto exampleVector = pybind11::bind_vector<std::vector<Example>>(m, "ExampleVector"); // 绑定二维vector,生成Example2DVector类 auto example2DVector = pybind11::bind_vector<std::vector<std::vector<Example>>>(m, "Example2DVector"); // 重载__getitem__,将内层vector转为ExampleVector返回 example2DVector.def("__getitem__", [](std::vector<std::vector<Example>>& self, size_t idx) -> std::vector<Example>& { // 先做索引越界检查,抛出Python风格的索引错误 if (idx >= self.size()) { throw pybind11::index_error("Index out of bounds"); } return self[idx]; }, pybind11::return_value_policy::reference_internal); }
代码解释
- 这里的lambda函数返回
std::vector<Example>&类型,pybind11会自动关联到我们之前已经绑定好的ExampleVector类(因为我们已经用PYBIND11_MAKE_OPAQUE和bind_vector处理过这个类型)。 return_value_policy::reference_internal指定返回的是原二维vector中内层vector的引用,这样在Python中修改返回的ExampleVector时,原二维vector的内容也会同步更新,完全符合你想要的引用传递预期。
验证Python代码
修改后再运行你的Python测试代码,就能正常访问了:
from ExModule import Example, ExampleVector, Example2DVector a = Example2DVector([ExampleVector([Example() for i in range(10)]) for i in range(10)]) b = a[4] print(type(b)) # 输出:<class 'ExModule.ExampleVector'> # 验证引用传递:修改b会影响原二维vector b[5] = Example() print(a[4][5]) # 正常输出,无报错
内容的提问来源于stack exchange,提问作者Kaleb Barrett
相关产品推荐
相关产品推荐

