Modules and the standard libraryChapter 78 of 114
Random Numbers
Pick, shuffle and sample, and know when random is not good enough.
The everyday functions
import random
value = random.randint(1, 6)
print(1 <= value <= 6)
print(random.choice(["rock", "paper", "scissors"]) in ["rock", "paper", "scissors"])
print(0 <= random.random() < 1)
print(1 <= random.uniform(1, 2) <= 2)Output
True True True True
The outputs are checked rather than printed, because they differ every run — and that is the point.
| Function | Gives |
|---|---|
randint(a, b) | a whole number, including both ends |
randrange(a, b) | a whole number, excluding b, like range |
random() | a float from 0 up to but not including 1 |
uniform(a, b) | a float between the two |
choice(seq) | one item |
choices(seq, k=n) | n items, with repeats |
sample(seq, k=n) | n items, no repeats |
shuffle(list) | reorders in place, returns None |
With and without repeats
import random
deck = ["a", "b", "c", "d"]
hand = random.sample(deck, 3)
print(len(hand), len(set(hand)))
rolls = random.choices(deck, k=6)
print(len(rolls))
print(set(rolls) <= set(deck))Output
3 3 6 True
sample gave three different cards. choices can repeat, which is right for dice and wrong for dealing.
Shuffling
import random
deck = [1, 2, 3, 4, 5]
result = random.shuffle(deck)
print(result)
print(sorted(deck))
print(len(deck))Output
None [1, 2, 3, 4, 5] 5
shuffle changes the list and returns None, like sort(). To keep the original, use random.sample(deck, len(deck)).
Seeding makes it repeatable
Same seed, same sequence. That is how you test code that uses randomness:
import random
random.seed(42)
first = [random.randint(1, 100) for _ in range(5)]
random.seed(42)
second = [random.randint(1, 100) for _ in range(5)]
print(first == second)
print(len(first), all(1 <= n <= 100 for n in first))Output
True 5 True
Without a seed, Python seeds from the system clock and entropy, so every run differs.
Weighted choices
import random
random.seed(0)
picks = random.choices(["common", "rare"], weights=[9, 1], k=1000)
print(set(picks) <= {"common", "rare"})
print(picks.count("common") > picks.count("rare"))Output
True True
The weights are relative, so [9, 1] means roughly nine to one. They do not have to add up to anything.
Independent streams
random.Random() gives you a generator that nothing else can disturb. Useful when one part of a program seeds for testing and another must not be affected:
import random
a = random.Random(1)
b = random.Random(1)
print([a.randint(1, 100) for _ in range(3)] == [b.randint(1, 100) for _ in range(3)])Output
True
Not for anything secret
random is a Mersenne Twister. Given enough output, its future values can be predicted, so it must never generate passwords, tokens or keys.
import secrets
token = secrets.token_hex(16)
print(len(token))
print(secrets.choice(["a", "b"]) in ["a", "b"])
print(secrets.randbelow(10) < 10)Output
32 True True
secrets has the same shape and uses the operating system's cryptographic source. The rule is simple: if guessing the value would matter, use secrets.
Test yourself
2 questionsWhat is the difference between randint(1, 6) and randrange(1, 6)?
Show the answer
randint can return 6; randrange cannot — One follows dice and the other follows range. Mixing them up is an off-by-one you will not see in testing.
Which module should generate a password reset token?
Show the answer
secrets — random is predictable from enough output. If guessing the value would matter, use secrets.
File Handling
Open a file safely, in the right mode, with the right encoding.