3042: add `as_tuple()` method to `PyList` r=davidhewitt a=samuelcolvin

From [benchmarks](25fcf98e12/benches/main.rs (L165-L192)), this is significantly faster than `PyTuple::new(py, the_list)`:

```
test list_as_tuple_direct        ... bench:         299 ns/iter (+/- 51)
test list_as_tuple_iterate       ... bench:         521 ns/iter (+/- 41)
```


Co-authored-by: Samuel Colvin <s@muelcolvin.com>
This commit is contained in:
bors[bot] 2023-03-12 13:56:01 +00:00 committed by GitHub
commit 30521c853d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 25 additions and 2 deletions

View File

@ -0,0 +1 @@
Add `as_tuple()` method to `PyList`, to more efficiently convert a lists to a tuples.

View File

@ -7,7 +7,7 @@ use std::convert::TryInto;
use crate::err::{self, PyResult}; use crate::err::{self, PyResult};
use crate::ffi::{self, Py_ssize_t}; use crate::ffi::{self, Py_ssize_t};
use crate::internal_tricks::get_ssize_index; use crate::internal_tricks::get_ssize_index;
use crate::types::PySequence; use crate::types::{PySequence, PyTuple};
use crate::{AsPyPointer, IntoPyPointer, Py, PyAny, PyObject, Python, ToPyObject}; use crate::{AsPyPointer, IntoPyPointer, Py, PyAny, PyObject, Python, ToPyObject};
/// Represents a Python `list`. /// Represents a Python `list`.
@ -290,6 +290,18 @@ impl PyList {
pub fn reverse(&self) -> PyResult<()> { pub fn reverse(&self) -> PyResult<()> {
unsafe { err::error_on_minusone(self.py(), ffi::PyList_Reverse(self.as_ptr())) } unsafe { err::error_on_minusone(self.py(), ffi::PyList_Reverse(self.as_ptr())) }
} }
/// Return a new tuple containing the contents of the list; equivalent to the Python expression `tuple(list)`.
///
/// This method uses `PyList_AsTuple` and so is significantly faster than `PyTuple::new(py, this_list)`.
pub fn as_tuple(&self) -> &PyTuple {
let py_tuple: Py<PyTuple> = unsafe {
let ptr = self.as_ptr();
let tuple_ptr = ffi::PyList_AsTuple(ptr);
Py::from_owned_ptr(self.py(), tuple_ptr)
};
py_tuple.into_ref(self.py())
}
} }
index_impls!(PyList, "list", PyList::len, PyList::get_slice); index_impls!(PyList, "list", PyList::len, PyList::get_slice);
@ -341,7 +353,7 @@ impl<'a> std::iter::IntoIterator for &'a PyList {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::types::PyList; use crate::types::{PyList, PyTuple};
use crate::Python; use crate::Python;
use crate::{IntoPy, PyObject, ToPyObject}; use crate::{IntoPy, PyObject, ToPyObject};
@ -870,4 +882,14 @@ mod tests {
"Some destructors did not run" "Some destructors did not run"
); );
} }
#[test]
fn test_list_as_tuple() {
Python::with_gil(|py| {
let list = PyList::new(py, vec![1, 2, 3]);
let tuple = list.as_tuple();
let tuple_expected = PyTuple::new(py, vec![1, 2, 3]);
assert!(tuple.eq(tuple_expected).unwrap());
})
}
} }