The Python standard library ships a set of power tools you reach for every day without installing anything: itertools to iterate, collections to tally and group, functools to cache and adapt functions, datetime plus zoneinfo for time, re for text, contextlib for resources, and dataclasses for typed records. The recurring mental model in this sheet is one promise: no dependency needed. Everything here comes with CPython itself, so there is nothing to pip install; you only import and go. Where this looks like the heavier sheets in the series, the contrast is the point: this is the “you already have this installed” companion that you reach for before pulling in a dependency, and it deliberately leaves heavy numeric work to the numpy and pandas sheets and HTTP to the requests sheet. Every command assumes the canonical imports (import itertools, from collections import Counter, from functools import lru_cache, import datetime as dt, from zoneinfo import ZoneInfo, import re, from contextlib import contextmanager, from dataclasses import dataclass), and every example was verified live on CPython 3.14.
itertools: Iterate Without Building Lists
itertools gives you composable, lazy iterator building blocks: they stream items on demand and never build a list until you ask, so you can chain, slice, group, and combine huge or infinite sequences in constant memory. The everyday four are chain (concatenate), islice (slice without indexing), product (replace nested loops), and groupby (run-length group adjacent equal keys, which means you must sort by the same key first); accumulate does running totals and pairwise gives a sliding two-window.
import itertools, operator
list(itertools.chain([1, 2], [3, 4])) # [1, 2, 3, 4] (flatten one level)
[(k, list(g)) for k, g in itertools.groupby(rows, key=lambda r: r[0])] # sort by key first!
list(itertools.islice(range(100), 2, 10, 2)) # [2, 4, 6, 8] (slice an iterator)
list(itertools.product([1, 2], ["a", "b"])) # nested loops -> all pairs
list(itertools.accumulate([1, 2, 3, 4])) # [1, 3, 6, 10] running totals
list(itertools.pairwise([1, 2, 3, 4])) # [(1,2),(2,3),(3,4)] (3.10+)See the itertools documentation. groupby only groups adjacent equal keys, so sort by the same key first.
collections: Tally, Group, Queue, Record
When a bare dict or list would need boilerplate, collections has a purpose-built container: Counter tallies occurrences as a multiset (with +, -, and .most_common), defaultdict auto-creates a missing key’s value so you skip the “if key not in d” dance, deque gives O(1) appends and pops at both ends (with an optional maxlen ring buffer), and namedtuple is a tiny immutable record with named fields. ChainMap layers several dicts into one fall-through view, which is perfect for config defaults plus overrides.
from collections import Counter, defaultdict, deque, namedtuple, ChainMap
Counter("mississippi").most_common(2) # [('i', 4), ('s', 4)] (a multiset)
dd = defaultdict(list); dd["x"].append(1) # {'x': [1]} no KeyError
dq = deque([1, 2, 3], maxlen=3); dq.appendleft(0) # deque([0, 1, 2]) right item falls off
dq.rotate(1) # shift the ring one slot right
Point = namedtuple("Point", ["x", "y"]); p = Point(1, 2) # p.x, p.y, p._asdict()
dict(ChainMap(overrides, defaults)) # layered lookups, top to bottomSee the collections documentation. Counter supports +, -, &, and |; deque ends are O(1).
functools: Cache, Adapt, and Compose Functions
functools adapts and wraps functions: lru_cache (or its @cache shorthand) memoizes a pure function so repeat calls are instant, partial pre-fills some arguments to make a new callable, reduce folds a sequence down to one value, and cached_property computes an attribute once and then returns the stored result. Always put @wraps(fn) on your own decorators so the wrapper keeps the original function’s name and docstring.
from functools import lru_cache, cache, partial, reduce, cached_property, wraps
import operator
@lru_cache(maxsize=None) # memoize: repeat calls hit the cache drawer
def fib(n): return n if n < 2 else fib(n - 1) + fib(n - 2)
# @cache is the 3.9+ shorthand for lru_cache(maxsize=None)
add10 = partial(operator.add, 10); add10(5) # 15 (pre-filled argument)
reduce(operator.mul, [1, 2, 3, 4], 1) # 24 (fold to one value)
# @cached_property over def val(self): ... computes once, then returns the stored value
# @wraps(fn) inside a decorator keeps the wrapped function's __name__ and __doc__See the functools documentation. fib.cache_info() reports hits and misses.
datetime: Instants, Dates, and Durations
A datetime is either naive (no timezone, ambiguous) or aware (carries a tzinfo); always prefer aware, and get “now” with dt.datetime.now(dt.UTC) rather than the deprecated utcnow(). Use dt.date for calendar dates, dt.timedelta for durations (datetime arithmetic yields a timedelta), fromisoformat/isoformat to round-trip ISO-8601, and strftime/strptime for custom layouts.
import datetime as dt
dt.datetime.now(dt.UTC) # aware "now" (dt.UTC is 3.11+ alias)
dt.date.today() # 2026-07-01 (date only, no time)
dt.timedelta(days=1, hours=2, minutes=30) # a duration to add or subtract
later - earlier # subtraction yields a timedelta
dt.datetime.fromisoformat("2026-07-01T09:30:00-05:00") # parse ISO-8601 -> aware
aware.strftime("%Y-%m-%d %H:%M %Z") # format out; strptime parses back inSee the datetime documentation. Avoid the deprecated utcnow() and utcfromtimestamp().
zoneinfo: Real IANA Time Zones
Since Python 3.9 the standard library ships zoneinfo, so you no longer need third-party pytz: ZoneInfo("Area/City") loads a real IANA zone (with full daylight-saving rules) that you attach with tzinfo= or convert to with .astimezone(...). The robust pattern is to store and compute in UTC and only convert to a local zone for display; on minimal systems without the OS zone database, pip install tzdata supplies it.
import datetime as dt, zoneinfo
from zoneinfo import ZoneInfo
ZoneInfo("America/Chicago") # IANA names, not fixed offsets
dt.datetime(2026, 7, 1, 9, 30, tzinfo=ZoneInfo("America/Chicago")) # naive -> aware
aware.astimezone(ZoneInfo("America/New_York")) # same instant, different wall time
dt.datetime.now(dt.UTC).astimezone(ZoneInfo("Europe/Paris")) # store UTC, render local
# ZoneInfo handles DST automatically across spring-forward / fall-back boundaries
len(zoneinfo.available_timezones()) # 598 (pip install tzdata if missing)See the zoneinfo documentation. Store and compute in UTC; convert to a local zone only for display.
re: Regular Expressions
Regular expressions match patterns in text: re.compile(r"...") builds a reusable pattern (use raw strings so backslashes survive), then .match anchors at the start while .search scans anywhere, and .findall/.finditer collect every hit. Capture substrings with groups (name them (?P<name>...) and read m.group("name") or m.groupdict()), and rewrite text with re.sub, whose replacement can be a literal string or a function called on each match.
import re
pat = re.compile(r"(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})") # compile once, reuse
pat.match(s) # anchored at the start; re.search scans
m.group("year"), m.groupdict() # pull out named captured groups
re.findall(r"\d+", "a1 b22 c333") # ['1', '22', '333'] every occurrence
re.sub(r"\s+", "_", s) # substitute literal or a callable
re.split(r"[,;]\s*", "a, b; c,d") # ['a', 'b', 'c', 'd'] split on a regexSee the re documentation and the Regular Expression HOWTO. Use raw strings so backslashes survive.
contextlib: Clean Resource Management
contextlib makes the with statement easy to build and use: the @contextmanager decorator turns a single-yield generator into a context manager (setup before the yield, teardown after), suppress(Exc) cleanly swallows an expected exception instead of a try/except that does nothing, and redirect_stdout reroutes prints. For a dynamic or unknown number of resources, ExitStack enters them as you go and unwinds them in reverse on exit.
import contextlib
from contextlib import contextmanager, suppress, redirect_stdout, ExitStack, closing
@contextmanager # setup before yield, teardown after
def tag(name): ...; yield ...
with suppress(FileNotFoundError): ... # swallow an expected exception
with redirect_stdout(buf): print("hi") # reroute print into a buffer
with ExitStack() as stack: stack.enter_context(open("a")) # dynamic count, LIFO unwind
with closing(thing) as t: ... # guarantee .close() on exit
class T(contextlib.ContextDecorator): ... # use as a with-block or an @T() decoratorSee the contextlib documentation. ExitStack enters resources as you go and unwinds them in reverse.
dataclasses: Typed Records Without Boilerplate
Decorate a class with @dataclass and annotate its fields, and Python generates __init__, __repr__, and __eq__ for you, so the class becomes a clean typed record. Use field(default_factory=list) for mutable defaults (never a bare = [], which is shared across instances), frozen=True for immutability, slots=True to drop __dict__, order=True for sortability, and __post_init__ plus field(init=False) for derived fields; asdict and replace convert and copy.
from dataclasses import dataclass, field, asdict, replace, fields
@dataclass # generates __init__, __repr__, __eq__
class Point: x: int; y: int = 0
tags: list[str] = field(default_factory=list) # "= []" is shared; use a factory
@dataclass(frozen=True, slots=True) # immutable + no __dict__
class Frozen: x: int # p.x = 5 -> FrozenInstanceError
@dataclass(order=True) # generates __lt__ and friends
class Ranked: score: int
asdict(p); replace(p, y=9) # to a dict; modified copy (original kept)See the dataclasses documentation. Set a derived field(init=False) field inside __post_init__.
Quick Reference
| When you need to… | Reach for | Module |
|---|---|---|
| Concatenate / slice / group / combine iterables lazily | chain, islice, groupby, product |
itertools |
| Running totals or a sliding window | accumulate, pairwise |
itertools |
| Count things | Counter |
collections |
| A dict that auto-creates missing values | defaultdict |
collections |
| Fast both-ends queue / ring buffer | deque(maxlen=...) |
collections |
| A tiny named record (tuple-backed) | namedtuple |
collections |
| Memoize a pure function | @lru_cache / @cache |
functools |
| Pre-fill arguments / fold a sequence | partial, reduce |
functools |
| Compute an attribute once | @cached_property |
functools |
| A timezone-aware “now” | datetime.now(dt.UTC) |
datetime |
| Real IANA time zones, convert between them | ZoneInfo, .astimezone |
zoneinfo |
| Match / capture / replace in text | re.compile, .search, re.sub |
re |
A reusable with-block |
@contextmanager |
contextlib |
| Swallow an expected error | suppress(Exc) |
contextlib |
A typed record with __init__/__repr__/__eq__ |
@dataclass |
dataclasses |
| Call | Returns (lazy iterator over) |
|---|---|
chain(a, b) |
items of a then b |
chain.from_iterable(iters) |
items of each inner iterable, flattened one level |
islice(it, start, stop, step) |
a sliced window (no negative indices) |
groupby(sorted_it, key) |
(key, group) pairs over adjacent equal keys |
product(a, b) |
every (x, y) pair (nested loops) |
accumulate(it[, func]) |
running reduction (sum by default) |
pairwise(it) |
consecutive overlapping pairs |
combinations(it, r) / permutations(it, r) |
r-length selections / orderings |
count(start, step) / cycle(it) / repeat(x, n) |
infinite (or n-long) generators |
batched(it, n) |
n-sized tuples (3.12+) |
| Type | Best for | Signature highlight |
|---|---|---|
Counter |
tally / multiset math | .most_common(k), +, -, &, | |
defaultdict |
grouping / accumulating | defaultdict(list), defaultdict(int) |
deque |
both-ends queue, ring buffer | appendleft, popleft, rotate, maxlen= |
namedtuple |
tiny immutable record | .field, ._asdict(), ._replace() |
ChainMap |
layered lookups (defaults + overrides) | first-found wins, top to bottom |
OrderedDict |
explicit reordering | .move_to_end(key) |
| Goal | Modern call | Avoid (deprecated/legacy) |
|---|---|---|
| Aware “now” in UTC | dt.datetime.now(dt.UTC) |
dt.datetime.utcnow() (deprecated 3.12) |
| Epoch -> aware UTC | dt.datetime.fromtimestamp(ts, dt.UTC) |
dt.datetime.utcfromtimestamp(ts) (deprecated 3.12) |
| Real time zone | ZoneInfo("Area/City") |
third-party pytz |
| Parse ISO-8601 | dt.datetime.fromisoformat(s) |
hand-rolled strptime for ISO input |
| Convert zone | aware.astimezone(ZoneInfo(...)) |
naive arithmetic across zones |
Appendix: Sample Code
Group records with sort + groupby (the classic gotcha)
groupby only groups adjacent equal keys, so you sort by the same key first.
import itertools
rows = [("eng", "ada"), ("sales", "bob"), ("eng", "cal"), ("sales", "dee")]
key = lambda r: r[0]
for team, members in itertools.groupby(sorted(rows, key=key), key=key):
print(team, [name for _, name in members])
# eng ['ada', 'cal']
# sales ['bob', 'dee']Tally and group in two lines
from collections import Counter, defaultdict
words = "the cat the dog the bird".split()
print(Counter(words).most_common(1)) # [('the', 3)]
by_len = defaultdict(list)
for w in words:
by_len[len(w)].append(w)
print(dict(by_len)) # {3: ['the', 'cat', 'the', 'dog', 'the'], 4: ['bird']}Memoize and pre-fill with functools
from functools import lru_cache, partial, reduce
import operator
@lru_cache(maxsize=None) # or just @cache (3.9+)
def fib(n):
return n if n < 2 else fib(n - 1) + fib(n - 2)
fib(30)
print(fib.cache_info()) # CacheInfo(hits=28, misses=31, ...)
add10 = partial(operator.add, 10)
print(add10(5)) # 15
print(reduce(operator.mul, range(1, 6), 1)) # 120Aware time, the correct way (store UTC, render local)
import datetime as dt
from zoneinfo import ZoneInfo
now_utc = dt.datetime.now(dt.UTC) # aware, never utcnow()
local = now_utc.astimezone(ZoneInfo("America/Chicago"))
print(local.strftime("%Y-%m-%d %H:%M %Z")) # e.g. 2026-07-01 04:30 CDT
# round-trip ISO-8601, with offset preserved
stamp = "2026-07-01T09:30:00-05:00"
when = dt.datetime.fromisoformat(stamp)
print(when.astimezone(ZoneInfo("America/New_York")).isoformat())
# 2026-07-01T10:30:00-04:00Parse with a compiled regex and named groups
import re
LOG = re.compile(r"(?P<ts>\d{4}-\d{2}-\d{2}) (?P<level>\w+) (?P<msg>.+)")
line = "2026-07-01 ERROR disk full"
m = LOG.match(line)
print(m.groupdict())
# {'ts': '2026-07-01', 'level': 'ERROR', 'msg': 'disk full'}
# replace with a callable
print(re.sub(r"\d+", lambda d: str(int(d.group()) * 2), "a1 b2 c3"))
# a2 b4 c6A context manager and an immutable dataclass
from contextlib import contextmanager, suppress
from dataclasses import dataclass, field, asdict, replace
@contextmanager
def timer(label):
import time
start = time.perf_counter()
try:
yield
finally:
print(f"{label}: {time.perf_counter() - start:.3f}s")
with timer("work"):
with suppress(ZeroDivisionError):
1 / 0 # swallowed, no traceback
@dataclass(frozen=True, slots=True)
class Point:
x: int
y: int = 0
tags: list[str] = field(default_factory=list) # safe mutable default
p = Point(1, 2)
print(asdict(p)) # {'x': 1, 'y': 2, 'tags': []}
print(replace(p, y=9)) # Point(x=1, y=9, tags=[])Behavior notes
- itertools is lazy. Calls like
chain,islice, andaccumulatereturn iterators that stream on demand; they only build a list when you wrap them inlist(...), so they run in constant memory. groupbyneeds a sort first. It groups only adjacent equal keys, so callsorted(rows, key=key)with the samekeybefore grouping or you will split runs apart.- Mutable defaults bite twice. A bare
tags: list = []in a dataclass is shared across all instances; always usefield(default_factory=list). The same fix applies todefaultdict(list). - Prefer aware datetimes.
dt.datetime.now(dt.UTC)is aware; the legacyutcnow()andutcfromtimestamp()are deprecated since 3.12 because they return naive values. zoneinforeplacespytz. Since 3.9 the stdlib loads real IANA zones with full DST rules; on minimal systems without the OS zone database,pip install tzdatasupplies the data.- Compile and reuse regexes.
re.compile(r"...")once, then reuse the pattern; raw strings keep backslashes intact, andre.sub’s replacement can be a literal string or a callable per match.
References
Standard library documentation (Python 3, current)
- The Python Standard Library index
itertools,collections,functoolsdatetime,zoneinfo,recontextlib,dataclasses
Deeper learning / how-to guides
- Regular Expression HOWTO and the
resyntax reference - Functional Programming HOWTO (covers itertools idioms)
strftime/strptimeformat codes- IANA Time Zone Database and
tzdataon PyPI - What’s New in Python 3.14