In order to shuffle the sequence uniformly, random.shuffle() needs to know how long the input is. A generator cannot provide this; you have to materialize it into a list:

lst = list(yielding(x))
random.shuffle(lst)
for i in lst:
    print i

You could, instead, use sorted() with random.random() as the key:

for i in sorted(yielding(x), key=lambda k: random.random()):
    print(i)

but since this also produces a list, there is little point in going this route.

Demo:

>>> import random
>>> x = [1,2,3,4,5,6,7,8,9]
>>> sorted(iter(x), key=lambda k: random.random())
[9, 7, 3, 2, 5, 4, 6, 1, 8]
Answer from Martijn Pieters on Stack Overflow
🌐
W3Schools
w3schools.com › python › ref_random_shuffle.asp
Python Random shuffle() Method
The shuffle() method takes a sequence, like a list, and reorganize the order of the items. Note: This method changes the original list, it does not return a new list. ... This example uses the function parameter, which is deprecated since Python ...
Top answer
1 of 7
41

In order to shuffle the sequence uniformly, random.shuffle() needs to know how long the input is. A generator cannot provide this; you have to materialize it into a list:

lst = list(yielding(x))
random.shuffle(lst)
for i in lst:
    print i

You could, instead, use sorted() with random.random() as the key:

for i in sorted(yielding(x), key=lambda k: random.random()):
    print(i)

but since this also produces a list, there is little point in going this route.

Demo:

>>> import random
>>> x = [1,2,3,4,5,6,7,8,9]
>>> sorted(iter(x), key=lambda k: random.random())
[9, 7, 3, 2, 5, 4, 6, 1, 8]
2 of 7
6

Depending on the case, if you know how much data you have ahead of time, you can index the data and compute/read from it based on a shuffled index. This amounts to: 'don't use a generator for this problem', and without specific use-cases it's hard to come up with a general method.

Alternatively... If you need to use the generator...

it depends on 'how shuffled' you want the data. Of course, like folks have pointed out, generators don't have a length, so you need to at some point evaluate the generator, which could be expensive. If you don't need perfect randomness, you can introduce a shuffle buffer:

from itertools import islice

import numpy as np


def shuffle(generator, buffer_size):
    while True:
        buffer = list(islice(generator, buffer_size))
        if len(buffer) == 0:
            break
        np.random.shuffle(buffer)
        for item in buffer:
            yield item


shuffled_generator = shuffle(my_generator, 256)

This will shuffle data in chunks of buffer_size, so you can avoid memory issues if that is your limiting factor. Of course, this is not a truly random shuffle, so it shouldn't be used on something that's sorted, but if you just need to add some randomness to your data this may be a good solution.

Discussions

python - Shuffling multiple iterators in order - Code Review Stack Exchange
I have a Python function w/ specified functionality, and two proposed approaches - which do you prefer, and why? (performance, simplicity, clarity, etc) (both use import random) PROBLEM: Need a pl... More on codereview.stackexchange.com
🌐 codereview.stackexchange.com
October 2, 2019
python - Shuffled range iterator - Stack Overflow
In python3 range doesn't return a list but an iterator. This is great and have some advantages such as not generating the whole list at once but only generating the elements when needed. Now supp... More on stackoverflow.com
🌐 stackoverflow.com
python - How to reset and shuffle a "next" iterator? - Stack Overflow
How can I reset it and shuffle it once it goes over all of the 360 values? So the above for loop will generate 360 shuffled numbers, reset after it goes over all the 360 values (each appearing only once), and generate another set of 360 values, and so on. ... May try to re-init the iterator- that's ... More on stackoverflow.com
🌐 stackoverflow.com
August 7, 2022
Adding random.shuffled to the random module (renamed thread) - Ideas - Discussions on Python.org
The first one requires a list (or a subtype of list) and does the sorting in-place, the second takes any iterable (including non-sequences) and returns a new sequence. Randomly shuffling a number of values, however, is a bit more complex. The random.shuffle function offers the equivalent of ... More on discuss.python.org
🌐 discuss.python.org
0
September 3, 2022
🌐
GitHub
gist.github.com › andres-erbsen › 1307752
Shuffle for python iterators. This works by holding `bufsize` items back and yielding them sometime later. This is NOT 100% random, proved or anything. · GitHub
#!/usr/bin/env python # -*- coding: utf-8 -*- def itershuffle(iterable): iterable = iter(iterable) buf = [] try: while len(buf) < 100: buf.append(next(iterable)) while True: i = (random.random() * 100) // 1 yield buf[i] try: buf[i] = next(iterable) except StopIteration: buf.pop(i) raise StopIteration except StopIteration: random.shuffle(buf) while buf: yield buf.pop() raise StopIteration
Top answer
1 of 4
7

While I do agree with others that Solution 2 is more readable with some improvements, there are also a few improvements that can be done on Solution 1.

  • It is unnecessary to construct lists from iterables (e.g., generator expressions) when all that is needed is an iterable. For example,

    _args      = [arg if type(arg)!=dict else arg.items() for arg in args]
    args_split = [arg for arg in zip(*_args)]
    

    Here, the unpacking operator * works on arbitrary iterables. So one can just do

    _args      = (arg if type(arg)!=dict else arg.items() for arg in args)
    args_split = [arg for arg in zip(*_args)]
    

    The parantheses keep the generator expressions without actually materializing them into lists.

  • It is better to use isinstance(arg, cls) rather than type(arg) == cls

  • Unpacking an iterable into a list can be done using list(iterable), which is more efficient than a list comprehension [arg for arg in iterable] that uses an explicit for-loop.
  • This expression

    args_types[i for i, arg in enumerate(args_shuffled)]
    

    can be rewritten using zip to avoid the need of indices:

    [cls(arg) for cls, arg in zip(args_types, args_shuffled)]
    

Following is an improved version of Solution 1

def ordered_shuffle(*args):
    arg_types = map(type, args)
    arg_elements = (arg.items() if isinstance(arg, dict) else arg for arg in args)
    zipped_args = list(zip(*arg_elements))
    random.shuffle(zipped_args)
    return [cls(elements) for cls, elements in zip(arg_types, zip(*zipped_args))]
2 of 4
12

functools.singledispatch

functools library includes the singledispatch() decorator. It lets you provide a generic function, but provide special cases based on the type of the first argument.

import functools
import random

@functools.singledispatch
def shuffle(arg, order):
    """this is the generic shuffle function"""

    lst = list(arg)
    return type(arg)(lst[i] for i in order)


@shuffle.register(dict)
def _(arg, order):
    """this is shuffle() specialized to handle dicts"""

    item = list(arg.items())
    return dict(item[i] for i in order)


def ordered_shuffle(*args):
    min_length = min(map(len, args))

    indices = random.sample(range(min_length), min_length)

    return [shuffle(arg, indices) for arg in args]

Usage:

a = (1, 2, {3: 4}, 5)
b = [(5,6), [7,8], [9,0], [1,2]]
c = {'arrow': 5, 'knee': 'guard', 0: ('x',2)}

ordered_shuffle(a, b, c)

Output:

[({3: 4}, 1, 2),
 [[9, 0], (5, 6), [7, 8]],
 {0: ('x', 2), 'arrow': 5, 'knee': 'guard'}]
🌐
The Python Coding Book
thepythoncodingbook.com › home › blog › beware python iterators that are not independent
Beware of Python Iterators That Are Not Independent
May 26, 2022 - Merge the remaining parts back into a single iterator and repeat the process until you’ve used up all the values in the original generator · This method is inspired by David Amos’s example in the article I mentioned in the introduction. You can start by creating the generator you’ve already used several times in this article and define a generator function using the yield keyword. I’ll use a script for this example rather than the console sessions I used earlier. # shuffle_generators.py n = 10 original_generator = (number for number in range(n)) def randomise_generator(original, length): while True: yield new_generator = randomise_generator(original_generator, n) for number in new_generator: print(number)
🌐
PyPI
pypi.org › project › shuffled
Client Challenge
August 6, 2016 - JavaScript is disabled in your browser · Please enable JavaScript to proceed · A required part of this site couldn’t load. This may be due to a browser extension, network issues, or browser settings. Please check your connection, disable any ad blockers, or try using a different browser
🌐
NumPy
numpy.org › doc › stable › reference › random › generated › numpy.random.Generator.shuffle.html
numpy.random.Generator.shuffle — NumPy v2.4 Manual
>>> rng = np.random.default_rng() >>> arr = np.arange(10) >>> arr array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) >>> rng.shuffle(arr) >>> arr array([2, 0, 7, 5, 1, 4, 8, 9, 3, 6]) # random
🌐
GeeksforGeeks
geeksforgeeks.org › python-ways-to-shuffle-a-list
Ways to shuffle a list in Python - GeeksforGeeks
May 3, 2025 - If we don't want to modify the original list then use random.sample() to generate a shuffled copy.
Find elsewhere
🌐
PYnative
pynative.com › home › python › random › python random.shuffle() function to shuffle list
Python random.shuffle() function to shuffle list
June 16, 2021 - Shuffle Python list using random.shuffle(). Also, learn how to shuffle String, arrays, dictionaries. Shuffle list in place and not in place.
🌐
datagy
datagy.io › home › python posts › python lists › python: shuffle a list (randomize python list elements)
Python: Shuffle a List (Randomize Python List Elements) • datagy
December 19, 2022 - Check out some other Python tutorials on datagy, including our complete guide to styling Pandas and our comprehensive overview of Pivot Tables in Pandas! The random.shuffle() function makes it easy to shuffle a list’s items in Python.
🌐
Stack Overflow
stackoverflow.com › questions › 50833256 › shuffled-range-iterator
python - Shuffled range iterator - Stack Overflow
2 14 15 1 4 5 3 0 7 10 11 6 8 9 12 13 >>> my_list=shuffled.Shuffled(16) >>> for i in my_list: print(i) ...
🌐
Spark By {Examples}
sparkbyexamples.com › home › python › shuffle list in python with examples
Shuffle List in Python with Examples - Spark By {Examples}
May 31, 2024 - At each iteration, it generates a random index within the range of the unshuffled elements and swaps the element at the current index with the element at the randomly generated index. This process is repeated until all the elements in the list have been shuffled.
Top answer
1 of 3
2

Generator endlessly shuffling and yielding:

def endless_shuffling(iterable):
    values = list(iterable)
    while True:
        random.shuffle(values)
        yield from values

Instead of your iter(all_angles), use endless_shuffling(all_angles) (and remove your own other shuffling).

One way to then get your list:

random_angles = endless_shuffling(range(-180, 180))
n_list = list(islice(random_angles, 1000))

If you give it an empty iterable and ask it for a value, it'll "hang", so either don't do that or guard against that case (e.g., with an extra if values: or with while values:).

I also tried a faster way to iterate than sending every value through a generator, but the shuffling dominates so it doesn't make a big difference:

with shuffling:
448.3 ms  endless_shuffling1
426.7 ms  endless_shuffling2

without shuffling:
 26.4 ms  endless_shuffling1
  5.1 ms  endless_shuffling2

Full code (Try it online!):

from random import shuffle
from itertools import chain, islice
from timeit import default_timer as time

def endless_shuffling1(iterable):
    values = list(iterable)
    while True:
        shuffle(values)
        yield from values

def endless_shuffling2(iterable):
    values = list(iterable)
    return chain.from_iterable(iter(
        lambda: shuffle(values) or values,
        []
    ))

funcs = endless_shuffling1, endless_shuffling2

for f in funcs:
    print(*islice(f('abc'), 21))

for i in range(6):
    for f in funcs:
        t0 = time()
        next(islice(f(range(-180,180)), 999999, 1000000))
        print('%5.1f ms ' % ((time() - t0) * 1e3), f.__name__)
    print()
    if i == 2:
        print('without shuffling:\n')
        def shuffle(x):
            pass
2 of 3
1

Try this. It re-shuffles the list if it recieves an error while calling next:

import random
all_angles = list(range(-180,180))
random.shuffle(all_angles)
next_angle = iter(all_angles)
n_list = []
for i in range(1000):
    try:
        n_list.append(next(next_angle))
    except StopIteration:
        random.shuffle(all_angles)
        next_angle = iter(all_angles)
        n_list.append(next(next_angle))
print(len(n_list)) # 1000
🌐
Note.nkmk.me
note.nkmk.me › home › python
Shuffle a List, String, Tuple in Python: random.shuffle, sample | note.nkmk.me
May 19, 2025 - In Python, you can shuffle (i.e., randomly reorder) sequences using random.shuffle() and random.sample(). While random.shuffle() modifies a list in place, random.sample() returns a new randomized list ...
🌐
Python.org
discuss.python.org › ideas
Adding random.shuffled to the random module (renamed thread) - Ideas - Discussions on Python.org
September 3, 2022 - The first one requires a list (or a subtype of list) and does the sorting in-place, the second takes any iterable (including non-sequences) and returns a new sequence. Randomly shuffling a number of values, however, is a bit more complex. The random.shuffle function offers the equivalent of ...
🌐
Reddit
reddit.com › r/rust › what's the best way to shuffle an iterator?
r/rust on Reddit: What's the best way to shuffle an iterator?
April 7, 2023 -

I need to get some random distinct elements inside a range. I don't need to shuffle all the items, so collecting the iterator into a `vec` and calling the ` shuffle` method on it is not desirable. Is there a way to iterate over the range in random order and only get the first `n` items?

EDIT with some context:
I have arbitrarily large collections of `BigInt` numbers and I need to retrieve `n` values in a range from `0` to numbers even of hundreds or thousands of bits. I cannot collect 1 googol items in a `Vec` only to retrieve some tenths or hundreds values. Of course, I could assume that the probability of getting the same number twice is negligible in this specific case but, since the range is arbitrary large I could also have a range of 10 numbers and I must take 9 of them. As a general solution I have started getting random numbers in the target range and manually check if this has been already taken in a previous iteration but I was looking for a lazy solution that could avoid unnecessary checks or allocations.

🌐
Python
docs.python.org › 3 › library › itertools.html
itertools — Functions creating iterators for efficient looping
February 24, 2026 - This module implements a number of iterator building blocks inspired by constructs from APL, Haskell, and SML. Each has been recast in a form suitable for Python. The module standardizes a core set...
🌐
NumPy
numpy.org › doc › 2.1 › reference › random › generated › numpy.random.shuffle.html
numpy.random.shuffle — NumPy v2.1 Manual
>>> arr = np.arange(9).reshape((3, 3)) >>> np.random.shuffle(arr) >>> arr array([[3, 4, 5], # random [6, 7, 8], [0, 1, 2]])