Python Standard Library
The batteries-included modules that mean you rarely need a dependency — collections, itertools, functools, pathlib, and more.
The standard library ships with every Python install, so each module here is a dependency you never add, pin or audit. The rows assume Python 3.10 or newer and flag anything later, such as tomllib (3.11) and itertools.batched (3.12).
Two habits make it pay off. Reach for the purpose-built structure instead of a hand-rolled one: Counter rather than a dict of counts, deque rather than list.pop(0). And know the complexity each one buys you, because that is usually why it exists.
collections
- defaultdict
d = defaultdict(list); d[k].append(x)— no KeyError; even readingd[k]inserts it- Counter
Counter(words).most_common(3)— frequency counting- c1 + c2 · c1 - c2
- add or subtract Counters; the result drops zero and negative counts
- deque
deque(maxlen=n)— O(1) appends/pops both ends; BFS queues- namedtuple
Point = namedtuple("Point", "x y")— lightweight records- OrderedDict
move_to_end(k)andpopitem(last=False)are O(1) — an LRU in a few lines- ChainMap
- layered lookups without copying:
ChainMap(cli_args, env, defaults)
itertools
- product · combinations
product(a, b)·combinations(xs, 2)·permutations(xs)- chain · groupby
chain(*lists)flattens one level;groupbygroups only ADJACENT items — sort by the key first- accumulate · pairwise
accumulate(xs)running totals;pairwise(xs)sliding pairs (3.10+)- islice(it, 10)
- first 10 items of any iterator, even an infinite one like
count() - batched(xs, 100)
- chunks of up to 100 as tuples (3.12+) — batch your API calls
- zip_longest(a, b)
- zip to the longest input, padding with
fillvalue; plainzipstops at the shortest
functools & operator
- @lru_cache(maxsize=None)
- cache results per argument tuple;
@cache(3.9+) is the same, shorter - reduce
reduce(operator.mul, xs, 1)folds a sequence;math.prod(xs)is clearer here- partial(f, x=1)
- pre-fill arguments to make a new callable — handy for callbacks and
map - @wraps(func)
- inside a decorator, keep the wrapped function’s name and docstring
- @cached_property
- compute a property once per instance, then store it as a plain attribute
- itemgetter · attrgetter
- fast sort keys:
key=itemgetter("age")insortedormax
Files, paths & formats
- pathlib
Path("a/b.txt").read_text()·.glob("*.py")·.exists()- p.rglob("*.py")
- recursive glob;
p.stem,p.suffix,p.with_suffix(".bak")for names - mkdir(parents=True)
- create a directory and its parents; add
exist_ok=Trueso reruns don’t fail - json
json.loads(s)·json.dumps(obj, indent=2)- json.dump(obj, f)
- write to a file (
json.load(f)reads);default=strserializes dates - csv.DictReader(f)
- rows as dicts keyed by the header; open the file with
newline="" - tomllib.load(f)
- read TOML such as
pyproject.toml(3.11+); open the file in binary mode - shutil
copytree,rmtree,move,which("git")— file operations os lacks- tempfile
TemporaryDirectory()in awithblock — removed when the block exits
Dates & time
- datetime
- aware UTC now:
datetime.now(timezone.utc);utcnow()is naive and deprecated (3.12) - fromisoformat(s)
- parse ISO 8601 strings; accepts most variants, including a trailing
Z, from 3.11 - zoneinfo
ZoneInfo("Europe/Berlin")— real time zones with DST, no pytz (3.9+)- timedelta
now + timedelta(days=7); subtracting datetimes gives one,.total_seconds()to compare- time.perf_counter()
- monotonic high-resolution clock for timing;
time.time()jumps when the clock is adjusted
Data & algorithms
- heapq
heappush/heappop— a min-heap;nlargest(k, xs)- heappush(h, (-p, x))
- max-heap by negating priorities; tuples compare element by element
- bisect
bisect_left(sorted_list, x)— binary search insert point- dataclasses
@dataclass— auto __init__/__repr__/__eq__- @dataclass(frozen=True)
- immutable and hashable;
slots=True(3.10+) saves memory per instance - enum
class Color(Enum): RED = 1— named constants;StrEnum(3.11+) for strings- statistics
mean,median,stdev,quantiles(xs, n=100)for percentiles- math
isclosefor floats,comb(n, k),prod,isqrt,inf- random
choice,sample(xs, k),shuffle; usesecretsfor anything security-related- decimal.Decimal
- exact decimal arithmetic for money; build from strings:
Decimal("0.10") - graphlib
TopologicalSorter(deps)then.static_order()— dependency order; raises on cycles- enumerate / zip
for i, x in enumerate(xs)·zip(a, b, strict=True)
Concurrency & processes
- subprocess.run
- pass a list, plus
check=True,capture_output=True,text=True— no shell injection - ThreadPoolExecutor
- parallel I/O:
pool.map(fetch, urls);ProcessPoolExecutorfor CPU-bound work - asyncio.run(main())
- start an event loop; inside,
await asyncio.gather(*tasks)runs coroutines concurrently - asyncio.TaskGroup
- structured concurrency: if one task fails, the others are canceled (3.11+)
- asyncio.timeout(5)
async with asyncio.timeout(5):— cancels the block after 5 s (3.11+)
Text & utilities
- re
re.findall,re.sub, named groups(?P<name>…); compile hot patterns once- f"{x=}"
- debug print showing
x=42;f"{n:,}"adds thousands separators,{v:.2f}rounds - shlex.split · quote
- split a command line the way a shell does · quote a value safely for one
- secrets.token_urlsafe()
- random tokens for reset links and API keys — never
randomfor these - hashlib.sha256(b)
.hexdigest()for checksums; never for passwords — use a slow KDF likescrypt- logging.basicConfig
level=logging.INFOplus aformat; call it once at startup- argparse
- CLI flags with help:
p.add_argument("--n", type=int)
From the command line
- python -m http.server
- serve the current directory on port 8000 — quick file sharing
- python -m venv .venv
- project-local environment; activate with
source .venv/bin/activate - python -m timeit
- micro-benchmark one statement, e.g.
"sum(range(1000))" - python -m cProfile
- profile a script;
-s cumtimesorts by cumulative time - breakpoint()
- drop into the debugger at that line;
PYTHONBREAKPOINT=0disables it
Recipes
Count the words in a file and print the top 10
from collections import Counter
from pathlib import Path
import re
words = re.findall(r"[a-z']+", Path("book.txt").read_text().lower())
for word, n in Counter(words).most_common(10):
print(f"{n:>6} {word}")most_common does the sorting, and {n:>6} right-aligns the counts in a 6-character column.
Run a shell command safely and capture its output
import subprocess
result = subprocess.run(
["git", "log", "--oneline", "-5"],
capture_output=True, text=True, check=True, timeout=30,
)
print(result.stdout)A list of arguments avoids shell injection, check=True raises CalledProcessError on a non-zero exit, and timeout stops a hung child.
Fetch many URLs concurrently with threads
from concurrent.futures import ThreadPoolExecutor, as_completed
from urllib.request import urlopen
def fetch(url):
with urlopen(url, timeout=10) as r:
return url, r.status, len(r.read())
urls = ["https://example.com", "https://www.python.org"]
with ThreadPoolExecutor(max_workers=8) as pool:
futures = [pool.submit(fetch, u) for u in urls]
for f in as_completed(futures):
print(f.result())Threads suit I/O-bound work because the GIL is released while waiting on the network; use ProcessPoolExecutor for CPU-bound work. f.result() re-raises any exception from the worker.
An LRU cache in a few lines
from collections import OrderedDict
class LRU:
def __init__(self, capacity):
self.capacity, self.data = capacity, OrderedDict()
def get(self, key):
if key not in self.data:
return None
self.data.move_to_end(key)
return self.data[key]
def put(self, key, value):
self.data[key] = value
self.data.move_to_end(key)
if len(self.data) > self.capacity:
self.data.popitem(last=False)move_to_end and popitem(last=False) are both O(1), which is the whole trick. To cache a function’s results, @lru_cache already does this.
Run tasks in dependency order
from graphlib import TopologicalSorter
deps = {"deploy": {"test", "build"}, "test": {"build"}, "build": {"fetch"}}
print(list(TopologicalSorter(deps).static_order()))
# ['fetch', 'build', 'test', 'deploy']Each key lists what must run before it, and a cycle raises CycleError instead of looping forever.
Questions about Python Standard Library Cheat Sheet
What is the difference between a list and a deque in Python?
A list is a dynamic array: indexing is O(1) and appending or popping at the end is amortized O(1), but inserting or removing at the front shifts every element, so list.pop(0) and list.insert(0, x) are O(n). collections.deque is a linked structure of fixed-size blocks with O(1) appends and pops at both ends, which makes it the right queue for breadth-first search or a sliding window, and deque(maxlen=n) discards the oldest items automatically. The trade-off is that indexing into the middle of a deque is O(n), so keep a list when you need random access.
When should I use lru_cache instead of cache?
functools.cache, added in Python 3.9, is an unbounded memoizer equivalent to lru_cache with maxsize set to None: it keeps every result forever, which suits pure recursive functions such as a memoized Fibonacci. lru_cache with a numeric maxsize keeps only the most recently used results and evicts the rest, so memory stays bounded when the space of inputs is large. Both require hashable arguments and are shared by all callers. On methods they also keep instances alive, so prefer module-level functions, or cached_property for per-instance values.
How do I work with dates and time zones in Python?
Use timezone-aware datetimes everywhere. datetime.now(timezone.utc) gives the current UTC time, and zoneinfo, in the standard library since 3.9, provides real IANA zones such as Europe/Berlin with daylight saving rules. Convert between zones with astimezone, serialize with isoformat, and parse back with datetime.fromisoformat, which accepts most ISO 8601 strings from Python 3.11. Avoid utcnow, which returns a naive value and is deprecated since 3.12, and never mix naive and aware values: ordering comparisons between them raise TypeError.
Is a Python dict ordered?
Yes. Since Python 3.7 the language guarantees that a dict preserves insertion order, so iterating, printing or serializing it yields keys in the order they were first added. Updating an existing key keeps its position, while deleting and re-adding it moves it to the end. OrderedDict is still useful for its extra methods, move_to_end and popitem with last=False, which make an LRU cache trivial, and because two OrderedDicts compare equal only when their order also matches. Sets, by contrast, make no ordering guarantee at all.