FWIW, we've already documented a clean way to do it, https://docs.python.org/3/library/random.html#random.shuffle , "To shuffle an immutable sequence and return a new shuffled list, use sample(x, k=len(x)) instead."
one downside of this is that it won't work on a non-sized iterable -- but I suppose that's not really an important use-case. It Is a use case, though, 'cause while a shuffled collection is going to be sized by definition, the source could be a generator or some other non-sized iterable. But not hard to "realize" the iterable first by making it a list or tuple.
My other question was about performance. Without looking at the code, I thought it *might* be faster to shuffle than build up a list with multiple samples. but in profiling, the sample version is only about 30% slower. (for this one example :-) )
In [13]: def shuffled_1(it):
...: result = list(it)
...: random.shuffle(result)
...: return result
In [14]: def shuffled_2(it):
...: return random.sample(it, k=len(it))
In [15]: %timeit shuffled_1(population)
3.71 ms ± 30.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [16]: %timeit shuffled_2(population)
4.23 ms ± 23.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
where:
In [17]: population
Out[17]: range(0, 10000)
So yeah, this is a fine solution.