Skip to content

Commit

Permalink
Merge #3044
Browse files Browse the repository at this point in the history
3044: Add `PyTuple::to_list` r=davidhewitt a=davidhewitt

Companion to #3043. I've included benchmarks which suggests that this method is indeed about 25% faster on my machine.

Co-authored-by: David Hewitt <1939362+davidhewitt@users.noreply.github.com>
  • Loading branch information
bors[bot] and davidhewitt authored Mar 18, 2023
2 parents 86b88d0 + 2c11b25 commit ebd417c
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 2 deletions.
26 changes: 25 additions & 1 deletion benches/bench_tuple.rs
Original file line number Diff line number Diff line change
@@ -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| {
Expand Down Expand Up @@ -61,13 +61,37 @@ 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);
c.bench_function("tuple_get_item", tuple_get_item);
#[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);
Expand Down
1 change: 1 addition & 0 deletions newsfragments/3044.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add `PyTuple::to_list()`, as a convenient and efficient conversion from tuples to lists.
22 changes: 21 additions & 1 deletion src/types/tuple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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());
})
}
}

0 comments on commit ebd417c

Please sign in to comment.