How to use the 'random' module in Python
Learn how to use Python's random module. Explore different methods, tips, real-world applications, and how to debug common errors.

Python's random module is essential for tasks that need unpredictable outcomes, such as simulations or games. It offers functions to generate numbers, select elements, and shuffle sequences with simple syntax.
You'll explore key techniques and tips, see real-world applications, and review debugging advice. This helps you master Python's random functionalities for your projects.
Basic usage of the random module
import random
random_number = random.random()
print(f"Random number between 0 and 1: {random_number}")--OUTPUT--Random number between 0 and 1: 0.7254897563178143
The random.random() function is the module's most basic tool, returning a pseudo-random floating-point number. Key things to know about its output are:
- The range is always [0.0, 1.0), including 0.0 but excluding 1.0.
- It serves as the building block for more complex random number generation.
You can easily adapt this function. For example, multiplying its result lets you generate numbers within a custom range, making it highly versatile for various applications.
Common random operations
Building on the foundation of random.random(), you can use more specific functions to generate integers, select from lists, or define precise floating-point ranges.
Generating random integers with randint()
import random
random_integer = random.randint(1, 100)
print(f"Random integer: {random_integer}")--OUTPUT--Random integer: 42
For generating random whole numbers, you'll use random.randint(). You provide two arguments, a and b, to set the boundaries of the integer range. It's important to know that this function's range is inclusive on both ends.
- This means the result can be
a,b, or any integer between them. - So,
random.randint(1, 100)will return a number from 1 to 100, including both endpoints.
Working with random floating-point numbers using uniform()
import random
random_float = random.uniform(5.0, 10.0)
print(f"Random float between 5.0 and 10.0: {random_float}")--OUTPUT--Random float between 5.0 and 10.0: 7.823481978264493
When you need a random float within a specific range, random.uniform() is your go-to function. It's more direct than scaling the output of random.random() because you simply pass two numbers, a and b, to define the range.
- The returned value can be equal to
aorb, making the range inclusive. - The order of the arguments doesn't matter;
uniform(5.0, 10.0)works just likeuniform(10.0, 5.0).
Making random choices from sequences with choice()
import random
fruits = ['apple', 'banana', 'cherry', 'durian', 'elderberry']
selected_fruit = random.choice(fruits)
print(f"Selected fruit: {selected_fruit}")--OUTPUT--Selected fruit: cherry
When you need to pick a single item from a list or another sequence, random.choice() is the perfect tool. It takes a sequence—like the fruits list in the example—and returns one of its elements at random. This function is incredibly useful for games, simulations, or any scenario where you need an unpredictable selection.
- It works with any non-empty sequence, including lists, tuples, and even strings.
- If you pass an empty sequence, it will raise an
IndexError, so ensure your sequence has items.
Advanced random techniques
Beyond picking single items, you can shuffle entire sequences, ensure your random results are repeatable, and model real-world statistical patterns for more sophisticated tasks.
Shuffling sequences with shuffle()
import random
cards = ['A', 'K', 'Q', 'J', '10']
print(f"Original order: {cards}")
random.shuffle(cards)
print(f"After shuffling: {cards}")--OUTPUT--Original order: ['A', 'K', 'Q', 'J', '10']
After shuffling: ['J', 'A', '10', 'Q', 'K']
The random.shuffle() function is ideal for randomizing the order of items within a list, much like shuffling a deck of cards. It's important to understand that this function modifies the sequence directly rather than creating a new, shuffled copy.
- It operates in-place, meaning it alters the original list and returns
None. - Because it modifies the sequence, you can only use it on mutable types like lists, not on immutable ones such as tuples.
Using random seeds for reproducibility
import random
random.seed(42)
print(random.random())
print(random.random())
random.seed(42)
print(random.random())--OUTPUT--0.6394267984578837
0.025010755222666936
0.6394267984578837
While randomness is usually the goal, sometimes you need predictable results for testing or sharing your work. The random.seed() function lets you achieve this by initializing the random number generator to a specific state. When you provide the same seed, you get the same sequence of random numbers every time.
- As the example shows, setting the seed to
42and then resetting it produces the exact same starting number. - If you don't set a seed, Python uses a less predictable source, often the system time, making each run unique.
Working with statistical distributions
import random
gaussian_values = [random.gauss(0, 1) for _ in range(5)]
exponential_value = random.expovariate(1.5)
print(f"Gaussian samples: {gaussian_values}")
print(f"Exponential sample: {exponential_value}")--OUTPUT--Gaussian samples: [0.496714153011, -0.138264301171, 0.647689996795, 1.523030600511, -0.234153749427]
Exponential sample: 0.2845772583537913
For more advanced modeling, you can generate numbers that follow specific statistical patterns. This is useful for simulating real-world scenarios where outcomes aren't uniformly distributed, like modeling measurement errors or arrival times.
- The
random.gauss()function pulls numbers from a Gaussian distribution, also known as the "bell curve." You define its shape with a mean (mu) and standard deviation (sigma). random.expovariate()generates numbers from an exponential distribution, which is great for modeling the time between events. It takes a rate parameter,lambda.
Move faster with Replit
Replit is an AI-powered development platform that transforms natural language into working applications. You can describe what you want to build, and Replit Agent creates it—complete with databases, APIs, and deployment.
For the random techniques covered in this article, Replit Agent can turn them into production-ready tools. You can use it to build:
- A deck-shuffling utility that uses
random.shuffle()to randomize a list of cards for a digital card game. - A decision-making tool that selects a random option from a user-defined list with
random.choice(). - A simulation dashboard that models event timings, like customer arrivals, using the
random.expovariate()function.
Turn your concepts into functional applications by describing your app idea. Replit Agent will write the code, test it, and fix issues automatically, all from your browser.
Common errors and challenges
Using the random module effectively means knowing how to sidestep common pitfalls, from subtle function differences to major security missteps.
Understanding the difference between randint() and randrange()
A frequent point of confusion is the subtle difference between random.randint() and random.randrange(). While both generate random integers, their range definitions are not the same, which can lead to off-by-one errors if you’re not careful.
random.randint(a, b)is inclusive on both ends, meaning the result can bea,b, or any integer in between.random.randrange(start, stop)works like Python's standardrange()function—it includes thestartvalue but excludes thestopvalue.
For example, randint(1, 5) can return 5, but randrange(1, 5) will only return numbers up to 4. This distinction is critical for avoiding unexpected behavior in loops or list indexing.
Forgetting to reset the seed between test runs
While setting a seed with random.seed() is great for making code reproducible, it can cause problems during testing if misused. If you set a seed at the beginning of a test suite and never change it, every test will run with the exact same sequence of "random" numbers. This creates a blind spot, as your code isn't being validated against different random inputs.
To ensure your tests are robust, you should either allow the seed to remain unset for true randomness or explicitly set a different seed for each test case that requires a unique but repeatable scenario.
Avoiding random for security-sensitive operations
This is the most critical pitfall to avoid: never use the random module for cryptographic or security-related purposes. The numbers it generates are pseudo-random, meaning they are produced by a deterministic algorithm. With enough information, an attacker could potentially predict the sequence of numbers, compromising your application's security.
For tasks like generating passwords, security tokens, or cryptographic keys, you must use Python's secrets module instead. It is specifically designed to produce cryptographically strong random numbers that are suitable for managing sensitive data.
Understanding the difference between randint() and randrange()
The difference between randint() and randrange() often trips developers up, leading to classic off-by-one errors. Because one function is inclusive and the other is exclusive, you might not get the number range you expect. See this common mistake in action.
import random
# Trying to get numbers from 1 to 10
numbers = [random.randrange(1, 10) for _ in range(5)]
print(f"Random numbers from 1-10: {numbers}")
The code aims for numbers 1 through 10 but uses randrange(1, 10). Since randrange() excludes the upper bound, the number 10 will never appear in the output. See how to correct this common mistake below.
import random
# For numbers 1-10 with randrange (end is exclusive)
numbers = [random.randrange(1, 11) for _ in range(5)]
# Or using randint which includes both endpoints
numbers_alt = [random.randint(1, 10) for _ in range(5)]
print(f"Random numbers from 1-10: {numbers}")
To get numbers up to 10, you need to adjust the range. The solution is using randrange(1, 11), since it excludes the upper bound. Alternatively, randint(1, 10) works because it's inclusive of both endpoints. This distinction is critical when generating list indices or setting loop boundaries, as it helps you prevent common off-by-one errors. Always double-check which function you're using to ensure your number range is correct.
Forgetting to reset the seed between test runs
Using random.seed() is great for reproducibility, but it can create a testing blind spot. When the seed isn't reset, your tests always use the same "random" values, failing to catch bugs that only appear with different inputs. See how this happens in the code below.
import random
def test_random_function():
random.seed(42)
result1 = random.random()
# Doing other operations that use random
random.random()
result2 = random.random()
assert result2 == 0.025010755222666936
This test is brittle because random.seed(42) makes the outcome predictable. The assert statement will always pass, but it fails to test how the function behaves with any other random values. The following code demonstrates a better approach.
import random
def test_random_function():
random.seed(42)
result1 = random.random()
# Reset seed for the next test
random.seed(42)
result2 = random.random()
assert result1 == result2
This corrected approach ensures your tests are both predictable and robust. By resetting the seed with random.seed(42) right before a random operation, you can reliably reproduce a specific outcome for verification. This is useful when:
- You need to test how your code handles a known "random" input.
- You want to isolate seeded behavior to a single test without affecting others.
This keeps your test suite from developing blind spots.
Avoiding random for security-sensitive operations
Never use the random module for security. Its numbers are predictable, making it unsafe for generating passwords or tokens. An attacker could potentially guess the sequence, creating a serious vulnerability. The code below demonstrates this insecure practice in action.
import random
import string
# Generating a security token (insecure)
chars = string.ascii_letters + string.digits
token = ''.join(random.choice(chars) for _ in range(20))
print(f"Security token: {token}")
This token is constructed by repeatedly calling random.choice(). Since the function's selections are based on a predictable algorithm, the entire token becomes guessable. See the correct way to handle this in the following example.
import secrets
import string
# Generating a security token (secure)
chars = string.ascii_letters + string.digits
token = ''.join(secrets.choice(chars) for _ in range(20))
print(f"Secure token: {token}")
The solution swaps random.choice() for secrets.choice() to build a truly secure token. Python's secrets module is designed for cryptographic operations, generating numbers that are unpredictable and safe from attackers. It's essential to use this module anytime you handle sensitive data.
Always reach for secrets when creating:
- Passwords or recovery codes
- Session tokens
- API keys
This simple change is critical for protecting your application and its users.
Real-world applications
Now that you know the common pitfalls, you can apply these functions correctly in practical scenarios, from generating random data to building simple games.
Generating secure passwords with choice()
The random.choice() function provides a quick way to generate a password by pulling characters from a specified set of letters, numbers, and symbols.
import random
import string
characters = string.ascii_letters + string.digits + string.punctuation
password = ''.join(random.choice(characters) for _ in range(12))
print(f"Generated password: {password}")
This snippet builds a 12-character string by repeatedly selecting from a character pool. First, it defines the `characters` string by combining all uppercase and lowercase letters, digits, and punctuation marks available in Python's `string` module.
- A generator expression then uses
random.choice()to pick a single character from the pool. - This selection happens 12 times, creating a sequence of random characters.
- Finally, the
''.join()method concatenates them into the final string.
Simulating a dice game with randint()
You can also use randint() to create simple games, such as a dice-rolling competition where each player's score is determined by random rolls.
import random
def roll_dice():
return random.randint(1, 6)
player1_score = sum(roll_dice() for _ in range(3))
player2_score = sum(roll_dice() for _ in range(3))
print(f"Player 1 scored: {player1_score}")
print(f"Player 2 scored: {player2_score}")
print(f"Winner: {'Player 1' if player1_score > player2_score else 'Player 2' if player2_score > player1_score else 'Tie'}")
This code pits two players against each other in a simple dice game. A helper function, roll_dice(), uses random.randint(1, 6) to return a number from 1 to 6, effectively simulating a single die roll.
- Each player's score is the total of three separate rolls. This is handled efficiently with a generator expression inside the
sum()function. - Finally, a nested conditional expression compares the final scores to declare a winner or announce a tie.
Get started with Replit
Turn your knowledge into a real tool. Tell Replit Agent: "Build a dice rolling simulator using random.randint()" or "Create a tool that randomly picks a winner from a list of names with random.choice()."
Replit Agent will translate your prompt into a finished application, handling the coding, testing, and deployment automatically. Start building with Replit.
Create and deploy websites, automations, internal tools, data pipelines and more in any programming language without setup, downloads or extra tools. All in a single cloud workspace with AI built in.
Create & deploy websites, automations, internal tools, data pipelines and more in any programming language without setup, downloads or extra tools. All in a single cloud workspace with AI built in.



%2520in%2520Python.png)