Generating random numbers in Python is a fairly straightforward activity which can be done in a few lines. There maybe many variations which you need to do ranging from decimal places, random numbers between a start and end number, and many more. We’ll go through many useful examples in this article.
The most basic way to generate random numbers in python is with the random library:
import random
num = random.random()
print( f"Random number between 0.0 and 1.0 ={num}\n")
Output as follows:

You’ll see that each time it is run it has a new random number.
Generating the same random number each time and why this matters
Sometimes, you may want to generate some random numbers, but then be able to generate the same random numbers each time. Now this may sound counter intuitive as the whole point of getting random numbers is so that, well, they are random. One scenario where you would like to regenerate the same random numbers is during testing. You may find some unusual behaviour and this is where you may want to replicate that behaviour for which you’l l need the same input. This is where you’d want to generate the same random number and you can do that in python using the seed function from the random library.
The idea behind the seed function is that you can think of it as a specific key which can be used to generate a series of random numbers which stems from a given key. Use a different seed and you’ll generate a different set of random numbers.
See the following example code which generates a random number between 1 and 0:
import random
random.seed(1)
for i in range(1,5):
num = random.random()
print( f"Random number between 0.0 and 1.0 ={num}\n")
Output as follows:

No matter how many times it is run, since the seed is the same each time, it generates the same numbers.
Python Random Number Between 1 and 10
Now that we know how to generate random numbers, how do you do it between two numbers? This is easily done in with either randint() for whole numbers or with uniform() for decimal numbers.
import random
num_int = random.randint(1,10)
print( f"Random whole number between 1 and 10 ={num_int}\n")
num_uni = random.uniform(1,10)
print( f"Random decimal number between 1 and 10 ={num_uni}\n")

Python Generate Random Numbers From A Range
Suppose you needed to generate random numbers from a range of data whether that be numbers, names or even a pack of cards. This can be done through selecting the random element in an array by choosing the index randomly. For example, if you had an array of 5 items, then you can randomly chose and index from 0 to 4 (where 0 is the index of the first item).
There is another and shorter way in python which is to use the random.choice() function. If you pass it an array, it will then randomly return one of the elements.
Here’s an example to randomly select a name from a list with both using the index (to show you how it works), and the much most efficient random.choice() library function:
import random
###### Selecing numbers from a range
names_list = [ "Judy", "Harry", "Sarah", "Tom", "Gloria"]
rand_index = random.randint( 0, len(names_list)-1 )
print( f"Randomly selected person 1 is = { names_list[ rand_index] }\n")
print( f"Randomly selected person 2 is = { random.choice( names_list) }\n")
And the output is different each time:

Generate Random String Of Length n in Python
If you want to generate a specific length string (e.g. to generate a password), both the random and the string libraries can come in handy where you can use it to create an easy password generator as follows:
import random, string
###### Create a random password
def generate_password( pass_len=10):
password = ""
for i in range(1,pass_len+1):
password = password + random.choice( string.ascii_letters + string.punctuation )
return password
print( f"Password generated = [{ generate_password(10) }] ")
This will output a new password each time between square brackets:

If there are specific characters you want to include or exclude, you can simply replace the string.punctuation with your own list/array of specific characters to be included
Random Choice Without Replacement In Python
Suppose you wanted to randomly select items from a list without repeating any items. For example, you have a list of students and you have to select them in a random order to go first in a specific activity. In many programming languages you may need to generate a random list and remember the previously selected items to prevent any repeated selections. In the random library, there is a function called random.sample() that will do all that for you:
import random
#### Select unique random elements
students = ["John", "Tom", "Paul", "Sarah", "July", "Rachel"]
random_order = random.sample( students, 6)
print(random_order)
This will generate a unique list without repeating any selections:

Sign up to the email list and get articles straight to your inbox. Plus get our free python one liner list!
” list=”237850″ redirect=”https://pythonhowtoprogram.com/thank-you-for-subscribing/” check_last_name=”off” layout=”top_bottom” first_name_fullwidth=”off” email_fullwidth=”off” _builder_version=”4.17.4″ _module_preset=”default” body_font=”|700|||||||” body_line_height=”1em” result_message_font=”|700|||||||” body_ul_line_height=”0.1em” custom_button=”on” button_bg_color=”#0C71C3″ button_border_color=”#FFFFFF” button_border_radius=”20px” button_letter_spacing=”0px” button_font=”|800|||||||” button_use_icon=”off” button_custom_margin=”0px||||false|false” button_custom_padding=”1px|1px|1px|1px|false|false” text_orientation=”center” background_layout=”light” custom_padding=”20px|30px|20px|30px|false|false” hover_enabled=”0″ border_radii=”on|3px|3px|3px|3px” box_shadow_style_button=”preset2″ box_shadow_vertical_button=”2px” global_colors_info=”{}” sticky_enabled=”0″][/mfe_send_fox]
Generate Date Between Two Dates in Python
In order to generate a date between two dates, this can be done by converting the dates into days first. This can be combined with the random.randint() in addition to the days of the date differences then adding back to the start date:
import random, datetime
#### Select a random date between two dates:
d1 = datetime.date( 2013, 2, 26 )
d2 = datetime.date( 2015, 12, 15 )
diff = d2 - d1
new_date_days = random.randint( 0, diff.days )
print( f"Random date is { d1 + datetime.timedelta( days=new_date_days ) }")
The output would be as follows:

Generate Random Temporary Filename in Python
A common need is to generate a random filename often for temporary storage. This might be for a log file, a cache file or some other scenario and can be easily done with the similar string generation as above. First a letter should be determined and then the remaining letters can be added with also numbers as well.
import random, string
def generate_random_filename( filename_len=10):
filename = ""
filename = filename + random.choice( string.ascii_lowercase )
for i in range(2, filename_len+1):
filename = filename + random.choice( string.ascii_lowercase + string.digits )
return filename
print( f"Random filename = [{ generate_random_filename( 10) }.txt]")
Output as follows:

There is in fact a specific python library though that does this which is even simpler:
import tempfile
filename = tempfile.NamedTemporaryFile( prefix="temp_" , suffix =".txt" )
print( f" Temporary filename is [{ filename.name }] ")
Output of the temporary filename generator is:

Conclusion
The random library has many uses from generating numbers to specific strings with a given length for password generation. Typically, these use cases sometimes have specialised libraries as there can be nuances (e.g for passwords, you may not want a repeating sequence which may be possible through random luck) which you can search for through pypi.org. However, many can be created with simple lines of code as demonstrated above. Send comments below or email me to ask further questions.
Subscribe
Not subscribed to our email list? Sign up now and get your next article in your inbox:
Related Articles
How To Build CLI Apps with Python Click
Intermediate
Every serious Python developer eventually needs to build a command-line interface. Whether it is a deployment tool, a data processing script, or a developer utility, a well-designed CLI makes the difference between a tool your team actually uses and one that sits forgotten. Python’s standard argparse module works, but it is verbose — you write 20 lines of setup code before you handle your first argument. Click is the modern alternative: decorator-based, expressive, and composable, it cuts that boilerplate in half and adds features argparse simply does not have.
Click was created by the team behind Flask and follows the same philosophy: explicit is better than implicit, but explicit does not have to be painful. You decorate a Python function with @click.command() and @click.option(), and Click handles argument parsing, help text, type conversion, validation, and error messages automatically. Install it with pip install click.
This article covers everything you need to build production-quality CLI tools with Click: basic commands and options, arguments, type validation, prompts, multi-command groups (subcommands), progress bars, and output formatting. By the end, we will build a complete file management CLI that demonstrates all these features working together.
Click Quick Example
Here is a complete Click CLI that greets a user, with an optional count parameter:
# quick_click.py
import click
@click.command()
@click.option('--name', default='World', help='Who to greet.')
@click.option('--count', default=1, type=int, help='Number of greetings.')
@click.option('--loud', is_flag=True, help='Use uppercase.')
def greet(name, count, loud):
"""A friendly greeting command."""
for _ in range(count):
message = f"Hello, {name}!"
if loud:
message = message.upper()
click.echo(message)
if __name__ == '__main__':
greet()
Run it from the terminal:
$ python quick_click.py --name Alice --count 3
Hello, Alice!
Hello, Alice!
Hello, Alice!
$ python quick_click.py --name Bob --loud
HELLO, BOB!
$ python quick_click.py --help
Usage: quick_click.py [OPTIONS]
A friendly greeting command.
Options:
--name TEXT Who to greet.
--count INTEGER Number of greetings.
--loud Use uppercase.
--help Show this message and exit.
Click generated a complete help page automatically from the function’s docstring and decorator metadata. The --help flag, type validation, and default values all come for free.
Options vs Arguments
Click distinguishes between two kinds of inputs: options (named flags like --name Alice) and arguments (positional inputs like a filename). Options are optional by default; arguments are required by default.
| Feature | Option (@click.option) | Argument (@click.argument) |
|---|---|---|
| Syntax | --flag value | Positional: cmd value |
| Required | Optional by default | Required by default |
| Help text | Shown in --help | Shown in usage line |
| Best for | Configuration, flags | Primary inputs (files, names) |
# options_arguments.py
import click
@click.command()
@click.argument('filename') # Required positional arg
@click.option('--output', '-o', default='-', # -o is a short alias
help='Output file (default: stdout)')
@click.option('--lines', '-n', default=10,
type=int, help='Number of lines to show.')
@click.option('--verbose', '-v', is_flag=True,
help='Show extra information.')
def head(filename, output, lines, verbose):
"""Show the first N lines of FILENAME."""
if verbose:
click.echo(f"Reading {filename}, showing {lines} lines")
try:
with open(filename) as f:
for i, line in enumerate(f):
if i >= lines:
break
click.echo(line, nl=False)
except FileNotFoundError:
click.echo(f"Error: {filename} not found", err=True)
raise SystemExit(1)
if __name__ == '__main__':
head()
Run it as python options_arguments.py myfile.txt --lines 5 --verbose. The -o short alias for --output is defined right in the option decorator. Click handles both -o file.txt and --output file.txt automatically.
Types and Validation
Click converts option and argument values to the specified Python type and shows a helpful error if the conversion fails. Beyond basic types, Click has specialized types like click.Path for file paths and click.Choice for enumerated values.
# types_demo.py
import click
@click.command()
@click.argument('input_file', type=click.Path(exists=True, readable=True))
@click.option('--format', 'output_format',
type=click.Choice(['json', 'csv', 'text'], case_sensitive=False),
default='text', help='Output format.')
@click.option('--max-size', type=click.IntRange(1, 1000),
default=100, help='Max size (1-1000).')
@click.option('--scale', type=float, help='Scaling factor.')
def process(input_file, output_format, max_size, scale):
"""Process INPUT_FILE with validation."""
click.echo(f"Processing: {input_file}")
click.echo(f"Format: {output_format}")
click.echo(f"Max size: {max_size}")
if scale:
click.echo(f"Scale: {scale}")
if __name__ == '__main__':
process()
When you pass an invalid value, Click provides a clear error message:
$ python types_demo.py myfile.txt --format xml
Error: Invalid value for '--format': 'xml' is not one of 'json', 'csv', 'text'.
$ python types_demo.py nonexistent.txt
Error: Invalid value for 'INPUT_FILE': Path 'nonexistent.txt' does not exist.
click.Path(exists=True) validates the file exists before your function even runs. click.IntRange(1, 1000) ensures the integer is within bounds. These validations happen automatically and produce user-friendly error messages — no manual error handling needed.
Interactive Prompts and Confirmation
For destructive operations, you often want to confirm with the user. Click provides @click.confirmation_option(), @click.password_option(), and click.prompt() for interactive input collection.
# prompts_demo.py
import click
@click.command()
@click.option('--username', prompt='Username',
help='Your username.')
@click.option('--password', prompt=True,
hide_input=True, confirmation_prompt=True,
help='Your password.')
@click.option('--database', prompt='Database name',
default='mydb', show_default=True)
def setup_connection(username, password, database):
"""Set up a database connection."""
click.echo(f"Connecting to {database} as {username}...")
click.echo(f"Password length: {len(password)} chars")
# In a real app, you'd use these to create a connection
click.echo("Connection configured successfully!")
@click.command()
@click.argument('filename')
@click.confirmation_option(prompt='Are you sure you want to delete this file?')
def delete_file(filename):
"""Permanently delete FILENAME."""
import os
try:
os.remove(filename)
click.echo(f"Deleted: {filename}", err=False)
except FileNotFoundError:
click.echo(f"File not found: {filename}", err=True)
if __name__ == '__main__':
setup_connection()
Run python prompts_demo.py and Click interactively prompts for each required value. The password is hidden during input (no echo to terminal) and asks for confirmation. The @click.confirmation_option adds a yes/no prompt before any destructive action — and automatically processes -y or --yes flags to skip the prompt in automated scripts.
Multi-Command Groups (Subcommands)
Real CLI tools like git and docker use subcommands: git commit, git push, docker build, docker run. Click’s @click.group() decorator creates this structure cleanly. Each subcommand is just another decorated function.
# groups_demo.py
import click
@click.group()
@click.option('--debug/--no-debug', default=False,
help='Enable debug output.')
@click.pass_context
def cli(ctx, debug):
"""Project management tool."""
ctx.ensure_object(dict)
ctx.obj['DEBUG'] = debug
@cli.command()
@click.argument('name')
@click.option('--template', default='basic',
type=click.Choice(['basic', 'flask', 'fastapi']),
help='Project template.')
@click.pass_context
def create(ctx, name, template):
"""Create a new project."""
if ctx.obj['DEBUG']:
click.echo(f"[DEBUG] Creating {name} with template {template}")
click.echo(f"Creating project '{name}'...")
click.echo(f"Template: {template}")
click.echo(f"Done! Run: cd {name} && python main.py")
@cli.command()
@click.argument('name')
@click.pass_context
def delete(ctx, name):
"""Delete a project."""
if ctx.obj['DEBUG']:
click.echo(f"[DEBUG] Deleting {name}")
click.confirm(f"Delete project '{name}'? This cannot be undone.", abort=True)
click.echo(f"Project '{name}' deleted.")
@cli.command()
@click.pass_context
def list_projects(ctx):
"""List all projects."""
click.echo("Projects:")
for project in ['api-service', 'data-pipeline', 'dashboard']:
click.echo(f" - {project}")
# Register the list command with a different name
cli.add_command(list_projects, name='list')
if __name__ == '__main__':
cli()
Run it as:
$ python groups_demo.py --help
Usage: groups_demo.py [OPTIONS] COMMAND [ARGS]...
Project management tool.
Options:
--debug / --no-debug Enable debug output.
--help Show this message and exit.
Commands:
create Create a new project.
delete Delete a project.
list List all projects.
$ python groups_demo.py create myapp --template flask
Creating project 'myapp'...
Template: flask
Done! Run: cd myapp && python main.py
$ python groups_demo.py --debug create myapp
[DEBUG] Creating myapp with template basic
Creating project 'myapp'...
The ctx.pass_context pattern passes a shared context object through all subcommands. The --debug flag is defined on the group level and passed down through context — this is the Click pattern for global flags that affect all subcommands.
Real-Life Example: A File Processing CLI
Here is a complete, practical CLI tool for processing text files — counting words, searching for patterns, and converting case — with progress bars for large files.
# filetools.py
import click
import re
from pathlib import Path
@click.group()
def cli():
"""File processing toolkit."""
@cli.command()
@click.argument('files', nargs=-1, type=click.Path(exists=True), required=True)
@click.option('--words/--no-words', default=True, help='Count words.')
@click.option('--lines/--no-lines', default=True, help='Count lines.')
@click.option('--chars/--no-chars', default=False, help='Count characters.')
def count(files, words, lines, chars):
"""Count words/lines/chars in FILES."""
total_w, total_l, total_c = 0, 0, 0
for filepath in files:
content = Path(filepath).read_text()
w = len(content.split())
l = content.count('\n')
c = len(content)
total_w += w; total_l += l; total_c += c
parts = []
if lines: parts.append(f"{l:>8} lines")
if words: parts.append(f"{w:>8} words")
if chars: parts.append(f"{c:>8} chars")
click.echo(f"{' '.join(parts)} {filepath}")
if len(files) > 1:
click.echo(f"{'':->40}")
click.echo(f"{total_l:>8} lines {total_w:>8} words total")
@cli.command()
@click.argument('pattern')
@click.argument('files', nargs=-1, type=click.Path(exists=True), required=True)
@click.option('--ignore-case', '-i', is_flag=True, help='Case-insensitive.')
@click.option('--count-only', '-c', is_flag=True, help='Print match count only.')
def search(pattern, files, ignore_case, count_only):
"""Search for PATTERN in FILES."""
flags = re.IGNORECASE if ignore_case else 0
for filepath in files:
content = Path(filepath).read_text()
matches = [(i+1, line) for i, line in enumerate(content.splitlines())
if re.search(pattern, line, flags)]
if count_only:
click.echo(f"{len(matches):>5} {filepath}")
else:
for lineno, line in matches:
click.secho(f"{filepath}:{lineno}: ", nl=False, fg='cyan')
# Highlight the match in yellow
highlighted = re.sub(pattern,
lambda m: click.style(m.group(), fg='yellow', bold=True),
line, flags=flags)
click.echo(highlighted)
if __name__ == '__main__':
cli()
Run as:
$ python filetools.py count README.md
45 lines 312 words README.md
$ python filetools.py search "import" *.py --ignore-case
filetools.py:1: import click
filetools.py:2: import re
filetools.py:3: from pathlib import Path
The nargs=-1 pattern on FILES accepts any number of file arguments, like the Unix convention. click.secho() combines echo with styled output (colors). The --ignore-case short alias -i matches grep’s convention, making the tool feel natural to Unix users.
Frequently Asked Questions
When should I use Click instead of argparse?
Use Click for new CLI tools — it is less verbose and more composable. argparse is already in the standard library and requires no installation, so it is better for simple scripts that need zero dependencies. Click shines for multi-command CLIs with many options, complex validation, interactive prompts, and colored output. If you are building something beyond a simple script, Click’s developer experience wins decisively.
How does Click compare to Typer?
Typer is built on top of Click and generates Click CLI definitions from Python function type hints. If you use type annotations throughout your code, Typer reduces Click boilerplate further — you get options and arguments from type hints with no decorators. The trade-off: Typer adds a dependency and is less flexible than Click for complex CLI patterns. Click is more explicit; Typer is more magic. Both are excellent choices.
How do I test Click commands?
Click provides a CliRunner for testing. Use from click.testing import CliRunner; runner = CliRunner(); result = runner.invoke(my_command, ['--option', 'value']). The result object has exit_code, output, and exception attributes. This lets you test CLI behavior in pytest without spawning a subprocess, and it works with input prompts by passing input='yes\n' to invoke().
Can Click read options from environment variables?
Yes. Set auto_envvar_prefix='MYAPP' on the group, and Click automatically reads MYAPP_OPTION_NAME from the environment for any option not provided on the command line. You can also set it per-option: @click.option('--api-key', envvar='API_KEY'). This is the standard pattern for 12-factor applications where configuration comes from the environment.
How do I package a Click app as a proper CLI command?
Add an entry_points section to your pyproject.toml: [project.scripts] mytool = "mypackage.cli:main". After pip install -e ., running mytool in the terminal invokes your Click function directly. This is the standard way to distribute CLI tools on PyPI — users install your package and get the command available system-wide.
Conclusion
We covered the full Click toolkit: defining commands with @click.command(), options with @click.option(), arguments with @click.argument(), type validation with click.Path and click.Choice, interactive prompts, multi-command groups with shared context using @click.pass_context, and colored output with click.secho(). The file processing CLI showed how to compose these features into a tool that feels like a native Unix command.
From here, explore Click’s progress bar support (click.progressbar()), file path handling with lazy file opening, and the CliRunner for testing. Click’s plugin system also allows distributing CLI extensions as separate packages — the same pattern used by Flask extensions.
Official documentation: click.palletsprojects.com
Related Articles
Further Reading: For more details, see the Python random module documentation.
Frequently Asked Questions
How do I generate a random number in Python?
Use random.randint(a, b) for integers or random.random() for a float between 0 and 1. Example: import random; num = random.randint(1, 100).
What is the difference between random and secrets?
The random module is for simulations and games but NOT for security. The secrets module provides cryptographically secure randomness for passwords, tokens, and security-sensitive applications.
How do I generate a random list of numbers?
Use [random.randint(1, 100) for _ in range(10)] for random integers. For unique numbers, use random.sample(range(1, 101), 10). For float arrays, use numpy.random.rand(10).
How do I set a random seed?
Call random.seed(42) before generating numbers. The same seed always produces the same sequence, useful for testing and reproducible experiments.
Can I generate numbers following a specific distribution?
Yes. Use random.gauss() for normal, random.uniform() for uniform. NumPy offers numpy.random.normal(), poisson(), binomial(), and many more.