From 2c11b25fee0e8b02e0a6a13d33e51c4123ba96f1 Mon Sep 17 00:00:00 2001 From: David Hewitt <1939362+davidhewitt@users.noreply.github.com> Date: Sun, 12 Mar 2023 18:13:08 +0000 Subject: [PATCH] Add `PyTuple::to_list` --- benches/bench_tuple.rs | 26 +++++++++++++++++++++++++- newsfragments/3044.added.md | 1 + src/types/tuple.rs | 22 +++++++++++++++++++++- 3 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 newsfragments/3044.added.md diff --git a/benches/bench_tuple.rs b/benches/bench_tuple.rs index 13da700e..07d7fe2d 100644 --- a/benches/bench_tuple.rs +++ b/benches/bench_tuple.rs @@ -1,7 +1,7 @@ use criterion::{criterion_group, criterion_main, Bencher, Criterion}; use pyo3::prelude::*; -use pyo3::types::{PySequence, PyTuple}; +use pyo3::types::{PyList, PySequence, PyTuple}; fn iter_tuple(b: &mut Bencher<'_>) { Python::with_gil(|py| { @@ -61,6 +61,28 @@ fn sequence_from_tuple(b: &mut Bencher<'_>) { }); } +fn tuple_new_list(b: &mut Bencher<'_>) { + Python::with_gil(|py| { + const LEN: usize = 50_000; + let tuple = PyTuple::new(py, 0..LEN); + b.iter(|| { + let _pool = unsafe { py.new_pool() }; + let _ = PyList::new(py, tuple); + }); + }); +} + +fn tuple_to_list(b: &mut Bencher<'_>) { + Python::with_gil(|py| { + const LEN: usize = 50_000; + let tuple = PyTuple::new(py, 0..LEN); + b.iter(|| { + let _pool = unsafe { py.new_pool() }; + let _ = tuple.to_list(); + }); + }); +} + fn criterion_benchmark(c: &mut Criterion) { c.bench_function("iter_tuple", iter_tuple); c.bench_function("tuple_new", tuple_new); @@ -68,6 +90,8 @@ fn criterion_benchmark(c: &mut Criterion) { #[cfg(not(any(Py_LIMITED_API, PyPy)))] c.bench_function("tuple_get_item_unchecked", tuple_get_item_unchecked); c.bench_function("sequence_from_tuple", sequence_from_tuple); + c.bench_function("tuple_new_list", tuple_new_list); + c.bench_function("tuple_to_list", tuple_to_list); } criterion_group!(benches, criterion_benchmark); diff --git a/newsfragments/3044.added.md b/newsfragments/3044.added.md new file mode 100644 index 00000000..fca0cae3 --- /dev/null +++ b/newsfragments/3044.added.md @@ -0,0 +1 @@ +Add `PyTuple::to_list()`, as a convenient and efficient conversion from tuples to lists. diff --git a/src/types/tuple.rs b/src/types/tuple.rs index 2895b116..a7183728 100644 --- a/src/types/tuple.rs +++ b/src/types/tuple.rs @@ -6,6 +6,7 @@ use crate::ffi::{self, Py_ssize_t}; #[cfg(feature = "experimental-inspect")] use crate::inspect::types::TypeInfo; use crate::internal_tricks::get_ssize_index; +use crate::types::PyList; use crate::types::PySequence; use crate::{ exceptions, AsPyPointer, FromPyObject, IntoPy, IntoPyPointer, Py, PyAny, PyErr, PyObject, @@ -213,6 +214,15 @@ impl PyTuple { length: self.len(), } } + + /// Return a new list containing the contents of this tuple; equivalent to the Python expression `list(tuple)`. + /// + /// This method is equivalent to `self.as_sequence().list()` and faster than `PyList::new(py, self)`. + pub fn to_list(&self) -> &PyList { + self.as_sequence() + .list() + .expect("failed to convert tuple to list") + } } index_impls!(PyTuple, "tuple", PyTuple::len, PyTuple::get_slice); @@ -444,7 +454,7 @@ tuple_conversion!( #[cfg(test)] mod tests { - use crate::types::{PyAny, PyTuple}; + use crate::types::{PyAny, PyList, PyTuple}; use crate::{Python, ToPyObject}; use std::collections::HashSet; @@ -918,4 +928,14 @@ mod tests { "Some destructors did not run" ); } + + #[test] + fn test_tuple_to_list() { + Python::with_gil(|py| { + let tuple = PyTuple::new(py, vec![1, 2, 3]); + let list = tuple.to_list(); + let list_expected = PyList::new(py, vec![1, 2, 3]); + assert!(list.eq(list_expected).unwrap()); + }) + } }