The standard library

Know what is already installed, so you stop writing it — dates, patterns, containers, randomness and numbers.

22of 24
≈ 13Min read
5Parts

datetime — and the one rule

Store and compute in UTC, convert only for display. A naive datetime — one with no timezone — is a bug that waits for a daylight-saving change or a server in another country, and then produces wrong answers instead of errors.

ISO 8601 (isoformat) is the interchange format: it sorts correctly as text, and every language reads it.

py
from datetime import datetime, date, timedelta, timezone

datetime.now(timezone.utc)                 # aware — always do this
datetime.now()                             # naive: no timezone, a future bug
datetime.now(timezone.utc) + timedelta(days=7, hours=3)

d = date(2026, 9, 22)
d.isoformat()                              # '2026-09-22'
date.fromisoformat("2026-09-22")
(date(2026, 12, 25) - d).days              # 94

datetime.strptime("22/09/2026", "%d/%m/%Y")   # parse
d.strftime("%A, %d %B %Y")                    # format

re — patterns, used sparingly

Always use a raw string (r"...") so the backslashes reach the regex engine. And the standard advice holds: a regular expression is the right tool for a pattern, and the wrong tool for a format — use a parser for HTML, JSON, CSV and dates.

py
import re

re.search(r"(\d{4})-(\d{2})", text)        # first match anywhere, or None
re.match(r"\d+", text)                      # only at the start
re.findall(r"\w+@\w+\.\w+", text)           # every match, as a list
re.sub(r"\s+", " ", text).strip()           # collapse whitespace
re.split(r"[;,]\s*", text)

m = re.search(r"(?P<year>\d{4})-(?P<month>\d{2})", text)
m.group("year"), m.start(), m.groups()

pattern = re.compile(r"...")                # compile it if it is in a loop

collections

deque with a maxlen is a ring buffer in one line — the last N log lines, a sliding window — and it drops from the other end automatically.

py
from collections import Counter, defaultdict, deque, namedtuple, ChainMap

Counter(words).most_common(3)
defaultdict(list)

q = deque([1, 2, 3], maxlen=100)
q.appendleft(0)       # O(1) at BOTH ends — a list is O(n) at the front
q.popleft()
q.rotate(1)

ChainMap(overrides, defaults)     # look up in order, without copying

random, and being able to reproduce it

random is a Mersenne Twister: excellent for simulations, useless for secrets, because the sequence is predictable from its state. Tokens, passwords and keys come from secrets.

py
import random

random.seed(42)                     # the same sequence every time — do this in tests
random.random()                     # 0.0 … 1.0
random.randint(1, 6)                # inclusive at both ends
random.choice(items)
random.sample(items, 3)             # without replacement
random.shuffle(items)               # in place, returns None

import secrets
secrets.token_urlsafe(32)           # for anything security-related. NOT random.

The rest of the shelf

Before reaching for a dependency, check here. Every package you add is a version to track, a licence to honour and an attack surface — and a surprising number of them wrap something that was already installed.

ModuleFor
math / statisticsisclose, gcd, hypot; mean, median, stdev, quantiles
itertools / functoolsLazy composition; cache, partial, reduce
json / csv / sqlite3Data in and out — yes, a real database is in the standard library
pathlib / os / shutilPaths, environment, copying and moving
textwrap / difflibWrapping and dedenting; diffs and get_close_matches
enum / decimal / fractionsNamed constants; money; exact ratios
unittest / doctestTests, with nothing to install
loggingInstead of print in anything that runs unattended

Practice