如何让Boost Python实现的数组正常支持Python的for循环?
问题描述
尝试用Boost Python实现自定义数组类,C++代码如下:
#include <boost/python.hpp> #include <memory> #include <vector> class MyObject { public: MyObject(int value) : value_(value) { } int value() const { return value_; } void set_value(int value) { value_ = value; } private: int value_; }; class MyArray { public: MyArray(int size) : array_(size) { for (auto& obj : array_) { obj = std::make_shared<MyObject>(0); } } std::shared_ptr<MyObject>& operator[](int index) { return array_[index]; } int size() const { return static_cast<int>(array_.size()); } private: std::vector<std::shared_ptr<MyObject>> array_; }; BOOST_PYTHON_MODULE(example) { namespace python = boost::python; python::class_<MyObject, std::shared_ptr<MyObject>>("MyObject", python::init<int>()) .add_property("value", &MyObject::value, &MyObject::set_value); python::class_<MyArray, std::shared_ptr<MyArray>>("MyArray", python::init<int>()) .def("__getitem__", &MyArray::operator[], boost::python::return_value_policy<boost::python::copy_non_const_reference>()) .def("__len__", &MyArray::size); }
数组在Python中基本功能正常:len()能正确返回长度,[]索引访问也正常,但用for循环遍历时无法在正确位置停止,触发C++运行时错误,试图访问不存在的myArray[3]元素。Python测试代码:
my_array = analyse_script.MyArray(3) my_array[0].value = 1 my_array[1].value = 2 my_array[2].value = 3 print(len(my_array)) # prints "3" for obj in my_array: print(obj.value) # prints "1", "2", "3"
需要修改哪些部分才能让该数组正常支持Python的for循环?
解决方案
Python的for循环依赖迭代器协议,或者要求__getitem__方法在索引超出范围时抛出**IndexError**异常。当前你的operator[]在C++里越界访问会直接触发运行时错误,而非抛出Python可识别的异常,所以Python无法判断何时停止迭代。
需要做以下修改:
1. 为MyArray::operator[]添加越界检查并抛出Python异常
修改C++代码中的operator[]方法,当索引越界时抛出Python的IndexError:
std::shared_ptr<MyObject>& operator[](int index) { if (index < 0 || index >= static_cast<int>(array_.size())) { PyErr_SetString(PyExc_IndexError, "Index out of bounds"); boost::python::throw_error_already_set(); } return array_[index]; }
这样当Python迭代到超出数组长度的索引时,会捕获到IndexError,自动停止循环,不会触发C++运行时错误。
2. (可选)实现迭代器协议(进阶方案)
如果想更规范地支持Python迭代模式,可以为MyArray实现__iter__方法,返回一个迭代器对象。不过对于简单序列类型,仅通过__getitem__抛出IndexError就足以满足for循环的需求。
修改后的完整C++代码如下:
#include <boost/python.hpp> #include <memory> #include <vector> class MyObject { public: MyObject(int value) : value_(value) { } int value() const { return value_; } void set_value(int value) { value_ = value; } private: int value_; }; class MyArray { public: MyArray(int size) : array_(size) { for (auto& obj : array_) { obj = std::make_shared<MyObject>(0); } } std::shared_ptr<MyObject>& operator[](int index) { if (index < 0 || index >= static_cast<int>(array_.size())) { PyErr_SetString(PyExc_IndexError, "Index out of bounds"); boost::python::throw_error_already_set(); } return array_[index]; } int size() const { return static_cast<int>(array_.size()); } private: std::vector<std::shared_ptr<MyObject>> array_; }; BOOST_PYTHON_MODULE(example) { namespace python = boost::python; python::class_<MyObject, std::shared_ptr<MyObject>>("MyObject", python::init<int>()) .add_property("value", &MyObject::value, &MyObject::set_value); python::class_<MyArray, std::shared_ptr<MyArray>>("MyArray", python::init<int>()) .def("__getitem__", &MyArray::operator[], boost::python::return_value_policy<boost::python::copy_non_const_reference>()) .def("__len__", &MyArray::size); }
内容的提问来源于stack exchange,提问作者BadJerry
相关产品推荐
相关产品推荐

