C.W.K.
Stream
Lesson 06 of 06 · published

The Rest You'll Meet — random, hashlib, uuid, secrets

~18 min · random, hashlib, uuid, secrets

Level 0Curious
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete

random — for non-security randomness

random.random(), random.randint(a, b), random.choice(seq), random.shuffle(list), random.sample(seq, k). Plenty for games, simulations, picking a sample from data. Seedable for reproducible tests via random.seed(42). Not cryptographically secure — don't use it for tokens, passwords, or anything an attacker shouldn't predict.

secrets — for security-sensitive randomness

secrets.token_hex(16), secrets.token_urlsafe(32), secrets.choice(seq). Same shape as random, but uses the OS's cryptographic RNG. Use this for session tokens, password reset codes, anything where guessability matters.

hashlib — content hashing

SHA-256 is the default everyday hash. hashlib.sha256(b"data").hexdigest(). Other algorithms exist but reach for SHA-256 unless you need otherwise. md5 and sha1 still work but are not collision-resistant — don't use them for security boundaries. Always hash bytes, not strings — encode first.

uuid — unique identifiers

uuid.uuid4() for random UUIDs (the most common case). uuid.uuid1() uses MAC + time (leaks both). Keep using uuid4 by default; the others have specific use cases. str(uuid.uuid4()) for the standard hyphenated string form.

Principle: The choice between random and secrets is "is this an attack surface or not?" If yes (auth, tokens, password reset, anti-CSRF), use secrets. If no (game logic, simulation, sampling), use random. Don't mix them up.

Code

random — for non-security uses·python
import random

random.seed(42)                       # reproducible
print(random.random())                # 0.6394...
print(random.randint(1, 100))         # 71

fruits = ["apple", "banana", "cherry", "date"]
print(random.choice(fruits))          # 'apple' (with seed 42)
print(random.sample(fruits, 2))       # ['banana', 'cherry']
random.shuffle(fruits)
print(fruits)                         # in random order

# Distributions
print(random.gauss(0, 1))             # normal distribution
print(random.uniform(10, 20))         # uniform in range
secrets — for cryptographic randomness·python
import secrets

# Token generation
print(secrets.token_hex(16))          # 32-char hex string (16 bytes)
print(secrets.token_urlsafe(32))      # URL-safe base64
print(secrets.token_bytes(8))         # 8 random bytes

# Random choice (cryptographically)
print(secrets.choice(["a", "b", "c"]))

# Comparison — random vs secrets for the same shape
import random
print("random:", random.randint(1, 100))      # predictable if seed known
print("secrets:", secrets.randbelow(100))     # not predictable

# Use secrets for: session tokens, password reset, API keys.
# Use random for: shuffling test data, picking a sample, simulations.
hashlib — content hashing·python
import hashlib

# SHA-256 is the everyday default
data = b"hello world"
h = hashlib.sha256(data)
print(h.hexdigest())                  # 'b94d27b9934d3e08a52e52d7da7dab...'
print(h.digest())                     # raw bytes

# Hashing a file in chunks (streaming for large files)
import pathlib
p = pathlib.Path("/etc/hostname")
h = hashlib.sha256()
with p.open("rb") as f:
    while chunk := f.read(8192):
        h.update(chunk)
print(h.hexdigest())

# Hashing a string — encode to bytes first
h = hashlib.sha256("안녕".encode("utf-8")).hexdigest()
print(h)
uuid — unique identifiers·python
import uuid

# uuid4 — random, the everyday choice
print(uuid.uuid4())                   # ad8a0a2c-...
print(str(uuid.uuid4()))              # standard hyphenated string

# uuid1 — based on MAC + time (leaks both — usually not what you want)
# uuid3, uuid5 — namespaced (deterministic given the same name)

# Common use: random IDs in databases
records = [{"id": str(uuid.uuid4()), "data": x} for x in ["a", "b", "c"]]
for r in records:
    print(r)
shutil — high-level file operations·python
import shutil
import tempfile
from pathlib import Path

# Copy
src = Path("/etc/hostname")
dst = Path("/tmp/hostname-copy")
shutil.copy(src, dst)
print(dst.read_text(encoding="utf-8"))
dst.unlink()

# Move (rename)
f = Path("/tmp/source.txt")
f.write_text("hi", encoding="utf-8")
shutil.move(str(f), "/tmp/dest.txt")
Path("/tmp/dest.txt").unlink()

# Remove a directory tree (recursive)
with tempfile.TemporaryDirectory() as td:
    sub = Path(td) / "nested" / "deeper"
    sub.mkdir(parents=True)
    (sub / "file.txt").write_text("hi", encoding="utf-8")
    # shutil.rmtree(td)  — would happen on context exit anyway

# Get terminal size, free disk space
print(shutil.get_terminal_size())
print(shutil.disk_usage("/"))

External links

Exercise

(a) Use secrets.token_urlsafe(24) to generate three session tokens and print them. (b) Hash the string "피파, 안녕" with SHA-256 (encode UTF-8 first) and print the hex digest. (c) Generate a random UUID4 and use it as a temp file name suffix in tempfile. (d) Use shutil.disk_usage('/') to print free space in human-readable form (GB).

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.