PyO3中如何简化Outer结构体的可迭代实现?无需额外Iter结构体
问题
我定义了如下简单的Rust结构体:
#[pyclass] struct Outer { inner: Vec<i32> }
根据PyO3文档,要让Outer支持迭代,需要实现__iter__方法,且该方法必须返回一个同时实现__iter__和__next__的类型。文档给出的方案是定义一个Iter结构体:
#[pyclass] struct Iter { iter: std::vec::IntoIter<i32> } #[pymethods] impl Iter { fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { slf } fn __next__(mut slf: PyRefMut<'_, Self>) -> Option<i32> { slf.iter.next() } }
随后在Outer中实现__iter__:
#[pymethods] impl Outer { pub fn __iter__(slf: PyRef<'_, Self>) -> PyResult<Py<Iter>> { let iter = Iter { iter: slf.inner.clone().into_iter(), }; Py::new(slf.py(), iter) } }
是否存在更简便的实现方式?比如,PyO3可自动将Vec<i32>转换为Python列表,有没有类似捷径,无需实现整个Iter结构体,只需类似如下写法即可完成迭代支持:
fn __iter__(...) { self.inner.iter().cloned() }
同时包裹正确的PyResult等必要处理?我可以接受额外结构体的实现方式,但觉得这一步或许可以省略。
解决方案
确实有更简洁的实现方式,无需手动定义迭代器结构体,以下两种方法都能满足需求:
方法一:复用Python列表的迭代器
既然PyO3可以自动将Vec<i32>转换为Python列表,我们可以直接把inner转成Python列表,再返回它的原生迭代器,让Python自行处理迭代逻辑:
#[pymethods] impl Outer { fn __iter__(slf: PyRef<'_, Self>) -> PyResult<PyObject> { let py_list: PyObject = slf.inner.clone().into_py(slf.py()); py_list.call_method0(slf.py(), "__iter__") } }
这种方式完全依赖Python原生列表的迭代能力,代码量大幅减少,不需要额外定义任何结构体。
方法二:使用PyO3的IntoPyIterator trait
PyO3提供了IntoPyIterator trait,它可以直接将Rust迭代器转换为Python可识别的迭代对象,底层已经封装了__iter__和__next__的实现,完全省去自定义迭代器的步骤,和你期望的写法高度一致:
use pyo3::prelude::*; use pyo3::iter::IntoPyIterator; #[pyclass] struct Outer { inner: Vec<i32> } #[pymethods] impl Outer { fn __iter__(slf: PyRef<'_, Self>) -> PyResult<impl IntoPyIterator<Item = i32>> { Ok(slf.inner.iter().cloned()) } }
需要注意的是,只要你的Rust迭代器实现了标准库的Iterator trait,就能通过IntoPyIterator转换为Python迭代对象,而Vec的迭代器天生满足这个条件,所以直接调用cloned()就能得到可转换的迭代器。
内容的提问来源于stack exchange,提问作者cadolphs
相关产品推荐
相关产品推荐

