Random Module

Python Modules

Python random Module

In this tutorial, we'll use the random module to generate random numbers, make random choices, shuffle items, and create repeatable test data.

Python's random module generates pseudo-random numbers and makes random selections. It is useful for games, simulations, test data, and shuffling collections.

It is included with Python:

import random

The values are pseudo-random: they are produced by an algorithm. They look unpredictable for ordinary programs, but they are not suitable for passwords, security tokens, or cryptography.

Random floating-point values

random.random() returns a float from 0.0 up to, but not including, 1.0:

import random

value = random.random()
print(value)

Every run will normally print a different value.

Use uniform(a, b) for a float between two limits:

temperature = random.uniform(18.0, 24.0)
print(round(temperature, 1))

Because floating-point values are approximations, do not assume the result has a fixed number of decimal places. Round only when displaying or when the problem specifically requires it.

Random integers

randint(a, b) includes both endpoints:

import random

dice_roll = random.randint(1, 6)
print(dice_roll)

randrange() follows the same start, stop, and step rules as range():

even_number = random.randrange(2, 11, 2)
print(even_number)

Possible results are 2, 4, 6, 8, and 10. The stop value itself is excluded, just as it is with range().

Choosing one item

Use choice() to select an item from a non-empty sequence:

import random

topics = ["lists", "functions", "classes"]
topic = random.choice(topics)

print(topic)

Calling choice([]) raises IndexError, so handle an empty sequence when it is possible:

topic = random.choice(topics) if topics else None

Choosing several items

sample() chooses unique items without replacement:

import random

names = ["Ada", "Grace", "James", "Linus"]
winners = random.sample(names, k=2)

print(winners)

No name can appear twice. k cannot be larger than the population.

choices() samples with replacement, so duplicates are possible:

colours = ["red", "green", "blue"]
draws = random.choices(colours, k=5)

print(draws)

It also supports weights:

status = random.choices(
    ["success", "failure"],
    weights=[9, 1],
    k=1,
)[0]

Weights express relative likelihood; they do not guarantee an exact ratio over a small number of calls.

Shuffling a list

shuffle() changes a list in place:

import random

cards = ["A", "K", "Q", "J"]
random.shuffle(cards)

print(cards)

It returns None. This is incorrect:

cards = random.shuffle(cards)

After that assignment, cards would be None.

To preserve the original list, copy it first:

shuffled_cards = cards.copy()
random.shuffle(shuffled_cards)

Repeating results with a seed

A seed makes a pseudo-random sequence repeatable:

import random

random.seed(42)

print(random.randint(1, 100))
print(random.randint(1, 100))

This is helpful in tests and debugging. It is usually undesirable in a game where each run should differ.

Avoid changing the module's global random state inside reusable functions. Create a dedicated generator instead:

import random


def repeatable_choice(items, seed):
    generator = random.Random(seed)
    return generator.choice(items)


print(repeatable_choice(["a", "b", "c"], 42))
print(repeatable_choice(["a", "b", "c"], 42))

Both calls return the same result, and other code using random is unaffected.

Generating random test data

You can combine random functions with comprehensions:

import random

generator = random.Random(7)
scores = [generator.randint(0, 100) for _ in range(10)]

print(scores)

Using a dedicated seeded generator makes a failed test case reproducible.

Randomness for security

Do not use random to create passwords, reset links, API keys, or session tokens. Its output can be predictable to an attacker.

Use the secrets module instead:

import secrets

token = secrets.token_urlsafe(32)
print(token)

For secure choices:

colour = secrets.choice(["red", "green", "blue"])

A practical example: roll several dice

import random


def roll_dice(count, sides=6, *, generator=None):
    if count < 0:
        raise ValueError("count cannot be negative")
    if sides < 2:
        raise ValueError("a die needs at least two sides")

    rng = generator or random
    return [rng.randint(1, sides) for _ in range(count)]


test_generator = random.Random(10)
print(roll_dice(3, generator=test_generator))

Accepting a generator keeps normal usage convenient while allowing deterministic tests.

Common random mistakes

  • Using random for security-sensitive values.
  • Forgetting that randint() includes both endpoints.
  • Forgetting that randrange() excludes its stop value.
  • Assigning the result of shuffle(), which is always None.
  • Asking sample() for more unique items than exist.
  • Calling choice() with an empty sequence.
  • Re-seeding before every call, which can repeatedly produce the same values.
  • Writing tests that sometimes fail because their random input is not reproducible.

Practice

1. Simulate rolling two six-sided dice and return their total. 2. Select three unique quiz questions from a list. 3. Shuffle a copy of a list without changing the original. 4. Produce the same five random integers whenever a supplied seed is reused. 5. Explain why a password generator should use secrets, not random.

Try related tasks in the [interactive Python coding exercises](/codingexercises).