"
This article is part of in the series
Last Updated: Saturday 27th December 2014

One of the most common tasks that requires random action is selecting one item from a group, be it a character from a string, unicode, or buffer, a byte from a bytearray, or an item from a list, tuple, set, or xrange. It's also common to want a sample of more than one item.

Don't do this when randomly selecting an item

A naive approach to these tasks involves something like the following; to select a single item, you would use randrange (or randint) from the random module, which generates a pseudo-random integer from the range indicated by its arguments:
[python]
import random

items = ['here', 'are', 'some', 'strings', 'of',
'which', 'we', 'will', 'select', 'one']

rand_item = items[random.randrange(len(items))]
[/python]

An equally naive way to select multiple items might use random.randrange to generate indexes inside a list comprehension, as in:
[python]
rand_items = [items[random.randrange(len(items))]
for item in range(4)]
[/python]
These work, but as you should expect if you've been writing Python for any length of time, there's a built-in way of doing it that is briefer and more readable.

Do this instead, when selecting an item

The pythonic way to select a single item from a Python sequence type — that's any of str, unicode, list, tuple, bytearray, buffer, xrange — is to use random.choice. For example, the last line of our single-item selection would be:

[python]
rand_item = random.choice(items)
[/python]

Much simpler, isn't it? There's an equally simple way to select n items from the sequence:

[python]
rand_items = random.sample(items, n)
[/python]

Randomly selecting from a set

sets are not indexable, meaning set([1, 2, 3])[0] produces an error. Therefore random.choice does not support sets, however random.sample does.

For example:

[python]
>>> from random import choice, sample
>>>
>>> # INVALID: set([1, 2, 3])[0]
>>> choice(set([1, 2, 3, 4, 5]))
Traceback (most recent call last):
File "", line 1, in
File "/random.py", line 275, in choice
return seq[int(self.random() * len(seq))] # raises IndexError if seq is empty
TypeError: 'set' object does not support indexing
[/python]

There are several ways to get around this, 2 of which are to convert the set to a list first, and to use random.sample which does support sets.

Example:

[python]
>>> from random import choice, sample
>>>
>>> # Convert the set to a list
>>> choice(list(set([1, 2, 3])))
1
>>>
>>> # random.sample(), selecting 1 random element
>>> sample(set([1, 2, 3]), 1)
[1]
>>> sample(set([1, 2, 3]), 1)[0]
3
[/python]

Duplicate Items

If the sequence contains duplicate values, each one is an independent candidate for selection. To avoid duplicates, one method would be to convert the list into a set, and back into a list. For example:

[python]
>>> my_list = [1, 1, 2, 2, 3, 3, 4, 4, 5, 5]
>>> my_set = set(my_list)
>>> my_list = list(my_set) # No duplicates
>>> my_list
[1, 2, 3, 4, 5]
>>> my_elem = random.choice(my_list)
>>> my_elem
2
>>> another_elem = random.choice(list(set([1, 1, 1])))
[/python]

About The Author