Learn by reading through in order

random and secrets — Choosing the Right Random Numbers

Learn Python's random and secrets modules from the ground up. Covers reproducible pseudorandom sequences with random.choice / randint / shuffle / sample plus seed, cryptographically safe tokens with secrets.token_hex / token_urlsafe, and when to use one vs. the other — with runnable practice exercises.

This article covers the two random-number modules in the standard library. random provides pseudorandom values for tests, games, and simulations (reproducible with seed), and secrets provides unpredictable values for passwords, reset tokens, and API keys (drawn from the OS's cryptographic random source). Picking the right one for the job is what the rest of the article is about.

How random and secrets Differ

Both modules return random numbers, but the mechanism and the use cases are completely different. random is built on the Mersenne Twister (a widely used pseudorandom-number generator), and calling random.seed(value) lets you reproduce the same sequence. secrets, on the other hand, draws from the OS's cryptographic random source (such as /dev/urandom), so there's no `seed` concept and the values are unpredictable every time.

For cases like tests or games — "it's fine if the result leaks" — reach for random. For passwords, tokens, and session IDs — "someone predicting the value would be a security incident" — reach for secrets.

When to Use random vs. secrets
randomReproducible pseudorandomsame sequence with seedGames / testssimulationssecretsUnpredictable crypto randomno seedTokens / passwordssession IDs
random gives you reproducible pseudorandom values — for tests and games. secrets draws from the OS's cryptographic source for unpredictable values — for tokens and password generation. Pick the right one for the job.
Aspectrandomsecrets
SourceMersenne Twister (algorithm)OS cryptographic source (/dev/urandom, etc.)
ReproducibilitySame sequence reproducible with seedNot reproducible (always different)
SpeedFastSlower (not a concern for the typical use case)
Best fitTests, games, simulationsTokens, passwords, session IDs

random — The Basics (randint / uniform / choice)

The basics of random are three things: a random integer / a random float / picking one element from a list. random.randint(low, high) returns an integer including both ends, random.uniform(low, high) returns a float in the given range, and random.choice(list) returns one element from the list.

Let's start without seed and see how these basic functions behave.

The Basic random Functions
random.randint(a, b)→ integer in [a, b]random.uniform(a, b)→ float in [a, b]random.choice(list)→ one element
randint is a random integer in range (both ends included), uniform is a random float in range, and choice picks one element from a list. The input shape and return shape differ for each, so pick the one that fits your need.
FunctionReturnTypical use
random.randint(a, b)Integer in [a, b] (both ends included)Dice rolls, test IDs
random.uniform(a, b)Float in [a, b]Noise, probabilistic simulations
random.choice(seq)One element of the sequencePick one item from a menu

Try the three basic random functions. The values are different every run, so we'll verify them by range and membership checks instead of exact comparison.

① Import random.

② Generate an integer in 1〜100 with random.randint and store it as n; generate a float in 0〜1 with random.uniform and store it as f; pick one element from ["Apple", "Banana", "Cherry"] with random.choice and store it as picked.

③ Print whether n is in 1〜100, whether f is in 0〜1, and whether picked is a member of the original list, formatted as int range: True / False, float range: True / False, in list: True / False.

(If your code runs correctly, the explanation will appear.)

Python Editor

Run code to see output

random — Seeded Reproducibility and Collection Operations

Now let's look at test reproducibility with `random.seed` and collection-operation functions (shuffle / sample). After calling random.seed(N), the same seed always produces the same sequence. random.shuffle(list) shuffles the list's elements in place, and random.sample(list, k) returns a new list of k unique elements drawn from it.

seed / shuffle / sample
random.seed(value)→ fix internal statereproduciblerandom.shuffle(list)→ shuffles in placereturns Nonerandom.sample(list, k)→ k unique itemsnew list
seed(value) fixes the internal state, so the same sequence repeats. shuffle rearranges the original list in place (returns None). sample returns a new list of k unique elements (the original is untouched).
FunctionReturnTypical use
random.seed(value)None (initializes internal state)Reproducible tests
random.shuffle(seq)None (shuffles the original list)Randomize the order of a list
random.sample(seq, k)New list of k unique elementsPick k respondents from a survey

Confirm that a value generated with seed 42 matches a value generated again after re-seeding to 42.

① Import random.

② Set the seed to 42, then call random.randint(1, 100) once and store it as n1.

③ Set the seed back to 42, then call random.randint(1, 100) again and store it as n2.

④ Print n1 == n2 as reproducible: True.

Python Editor

Run code to see output

See the difference between shuffle (rearranges the list itself) and sample (returns a new list).

① Set the seed to 7 and prepare cards = [1, 2, 3, 4, 5].

② Call random.shuffle(cards) to rearrange the contents of cards in place. Print the rearranged cards as after shuffle: ◯◯.

③ Re-seed to 7, then call random.sample([1, 2, 3, 4, 5], 3) to pick 3 unique elements, and print the result as picked 3: ◯◯.

Python Editor

Run code to see output

secrets — Strong Random for Security

The random module from the previous sections is a pseudorandom generator for tests — if its internal state leaks, the next values can be predicted. For things like password reset tokens, API keys, and session IDs"if an attacker can predict the value, you have a vulnerability" — pick the `secrets` module, which uses the OS's cryptographic random source.

secrets has a much smaller API than random — it's mostly helpers that turn random bytes into hex strings or URL-safe strings.

FunctionReturnTypical use
secrets.token_bytes(n)n random bytesCrypto keys, internal binary tokens
secrets.token_hex(n)Hex string of length 2*nLogin session IDs
secrets.token_urlsafe(n)URL-safe string (Base64-ish)Embed in reset-link URLs
secrets.choice(seq)One element from seq, cryptographicallyWhen the display order shouldn't be predictable
secrets.compare_digest(a, b)True / False (timing-attack resistant)Hash / token comparison (use instead of ==)

Generate security tokens with secrets and check their length and character set. The actual values change every run, so we verify properties like length.

① Import secrets.

② Generate a 16-byte hex token, print its length as hex length: ◯, and then check it contains only characters in 0-9 and a-f with hex is hex only: True / False.

③ Generate a 16-byte URL-safe token and print its length as urlsafe length: ◯.

Python Editor

Run code to see output
QUIZ

Knowledge Check

Answer each question one by one.

Q1Which module should you reach for to generate a password reset token?

Q2After calling random.seed(42) and then random.randint(1, 100), what happens if you do the same thing again?

Q3Which is the best option when you want a randomly shuffled list to be reproducible for tests?