Quick Reference · the whole language on one page · current as of 3.14 · v4 refined
python 3.14 cheat sheet v4 · single source of truth
Everything in Python is a name bound to an object. Master four mental models — name lookup (LEGB), object mutability, the iteration protocol, and the concurrency map — and the rest is detail. This edition folds in what actually changed in 3.14 (released 7 Oct 2025): t-strings, deferred annotations, an officially-supported free-threaded build, and subinterpreters — see Part 0. v4 deepens every section: bytes & encoding, the advanced typing vocabulary, asyncio.to_thread, subinterpreter queues, TOML, and the everyday stdlib corners the earlier sheets skipped. v4 splits Type Hints and Async into focused basics/advanced pairs and rebalances the columns.
core syntax & types
data structures
control & functions
oop · typing · concurrency
new in 3.14
gotcha
★ most common
Distilled & cross-checked against: docs.python.org/3.14 (What's New · Language & Library Reference) · PEPs 649·734·750·758·765·768·779·784 (plus 572·634·695·701·703) · realpython.com · astral.sh · python.org "What's New" 3.10–3.14. Verified 2026-08-27 against Python 3.14.7.
Scope: this is the Python 3.14 release sheet — it foregrounds what changed in 3.14. For the version-agnostic general language reference (valid 3.12+), see the separate Python cheat sheet. The two are maintained independently.
Part 0 · New in Python 3.14
N1Template strings (t-strings)PEP 750
tmpl = t"Hi {name}!"3.14Same syntax as an f-string, t prefix — but it does not render to a str.
type(tmpl) → string.templatelib.TemplateYou get static parts + Interpolation objects to inspect first.
for part in tmpl: ...Escape / validate / transform each value before building output.
from string.templatelib import InterpolationThe safe way to assemble HTML, SQL, or shell — no injection by default.
t"…" + f"…"errort-strings only combine with other t-strings — never mixed with str/bytes.
for part in tmpl: match part:Iterating yields alternating str and Interpolation parts — the way you process one.
tmpl.strings · tmpl.interpolations · tmpl.valuesStatic text · the Interpolation objects (.value .expression .conversion .format_spec) · raw values.
N2Deferred annotationsPEP 649 / 749
def f(a: Node) -> Node: # no "Node" quotes3.14Annotations are evaluated lazily, only when read — forward refs just work.
import annotationlibNew stdlib module to introspect annotations safely.
get_annotations(f, format=Format.STRING)Read them as VALUE / FORWARDREF / STRING — no import-time cost.
from __future__ import annotationsNo longer needed for forward refs — 3.14 does it natively.
N3Free-threaded PythonPEP 779
$ python3.14t3.14The no-GIL build is now officially supported (was experimental) — an opt-in build, still not the default.
sys._is_gil_enabled() → FalseThreads run Python bytecode truly in parallel on this build.
per-object locking + biased refcountingReplaces the single global lock. Single-thread cost ≈ 5–10%.
C extensions need an ABI rebuildnotePure-Python code benefits immediately; native wheels must be recompiled.
N4Multiple interpretersPEP 734
from concurrent import interpreters3.14Subinterpreters reach the stdlib — process-like isolation, thread-like cost.
interp = interpreters.create()Each has its own state & its own GIL (or none, on the free-threaded build).
interp.exec("print('hi from a sub!')")No sharing by default — you pass data across explicitly.
InterpreterPoolExecutor()In concurrent.futures — a pool backed by subinterpreters.
q = interpreters.create_queue()The supported channel between interpreters — items are mostly copied via pickle.
interp.prepare_main(out=q); interp.call(fn)Inject names into the sub’s __main__; call() runs a function in it.
N5except & finally changesPEP 758 / 765
except TimeoutError, ConnectionError:3.14PEP 758 — brackets optional for multiple types when there's no as.
except* TypeError, ValueError:Same bracket-free form works for except* groups.
return / break / continue in a finally3.14PEP 765 now raises a SyntaxWarning — it can silently swallow exceptions.
with as e: ... # still needs ()noteThe brackets stay required whenever you bind with as.
N6Stdlib & tooling polish3.14
from compression import zstd3.14PEP 784 — Zstandard in the stdlib: zstd.compress(data).
p.copy/copy_into · p.move/move_into · p.infoNew pathlib.Path file ops (+ *_into a dir) & a cached .info for type/stat() lookups.
$ python -m pdb -p 1234PEP 768 — attach a debugger to a live process by PID; programmatic form is sys.remote_exec(pid, script).
$ python -m asyncio ps PIDInspect the running task tree of an async process (also pstree PID).
$ python -m json data.jsonPreferred CLI — python -m json.tool is now soft-deprecated. REPL syntax highlighting is on by default (_colorize.set_theme()).
uuid.uuid6/7/8()New UUID versions; existing UUID generation is faster. Opt-in tail-call interpreter (--with-tail-call-interp) adds ~3–5% geomean.
N6+Builtins & concurrency3.14
map(f, a, b, strict=True)3.14Like zip() — raises if the iterables differ in length.
float.from_number(x) · complex.from_number(x)3.14Strict numeric conversion — TypeError if x isn't a real number.
heapq.heapify_max(h)3.14Real max-heaps at last: also heappush_max / heappop_max / heapreplace_max.
memoryview[int]3.14memoryview is now a generic (subscriptable) type for annotations.
multiprocessing → forkserver3.14Behavior change: on Unix (not macOS) the default start method is now forkserver, not fork — globals are no longer inherited; guard setup under if __name__ == "__main__".
from concurrent import interpreters3.14PEP 734 — multiple in-process interpreters; pairs with InterpreterPoolExecutor.
N7f-string vs t-stringwhen to reach for which
f"{user}" → str★Renders immediately to a finished string. Use for logs, messages, display.
t"{user}" → Template3.14Defers rendering — you intercept values first. Use for HTML / SQL / shell.
html(t"<b>{user}</b>")A t-string processor can auto-escape every interpolation — injection-safe by construction.
rule of thumbTrusted output → f-string. Untrusted values → t-string.
Part I · Foundations
01Run & Environmentstart here
python --versionCheck interpreter version.
python★Interactive REPL (colorized, multiline in 3.13+).
python script.py★Run a script file.
python -m moduleRun a module as a script (e.g. -m http.server).
python -c "print(1+1)"Run a one-liner.
python -i script.pyRun then drop into the REPL for inspection.
python -X dev script.pyDev mode — extra runtime warnings & checks.
if __name__ == "__main__":★Only run when executed directly, not on import.
$ python -X importtime=2 script.py3.14Import timing — =2 now flags already-cached modules.
$ python -O script.py · -OOStrip asserts (-O) and docstrings (-OO) for release builds.
02Variables & Assignmentnames → objects
x = 5★Bind a name to an object; no declaration, dynamic typing.
a, b = b, a★Tuple unpacking — the classic swap.
x += 1★Augmented assignment (-=, *=, /=, //=, **=, %=).
first, *rest = numsStarred unpacking; works in any position but once.
_, x, _ = row_ is the convention for "don't care".
if (n := len(data)) > 3:★Walrus := — assign inside an expression (3.8+).
x = y = []aliasChained assignment binds both names to the same object.
03Numbers & Operatorsarithmetic & logic
7 / 2 → 3.5 7 // 2 → 3★True division always floats; // floors (toward −∞).
7 % 2 → 1 divmod(7, 2)Modulo; divmod returns (quotient, remainder).
2 ** 10 pow(2, 10, m)Power; 3-arg pow = fast modular exponent.
x == y x is y★== value equality; is identity — use is only for None/singletons.
a and b a or b not aShort-circuit; returns an operand, not just a bool.
0 < x < 10Chained comparisons evaluate each operand once.
& | ^ ~ << >>Bitwise: and, or, xor, invert, shifts.
int("ff", 16) 0b101 0o17 0x1FBase conversion & literal prefixes; 1_000_000 separators OK.
04Numeric Precision & Mathfloat · decimal · math
0.1 + 0.2 == 0.3 → Falseieee-754Binary floats can't represent most decimals exactly.
math.isclose(a, b)★The correct float comparison.
Decimal("0.1") + Decimal("0.2")Exact decimal arithmetic (money) — construct from strings.
Fraction(1, 3)Exact rational arithmetic.
round(2.5) → 2 round(3.5) → 4Banker's rounding — half rounds to the nearest even.
math.floor · ceil · sqrt · log · pi · inf · nanCore math module; nan != nan — test with math.isnan.
statistics.mean · median · stdevDescriptive stats without NumPy.
10**100Python ints have arbitrary precision — no overflow.
05Strings & Methodsimmutable text
s.split(",") "-".join(xs)★Split / join — every method returns a new string.
s.strip() · lstrip() · rstrip()★Trim whitespace (or given chars).
s.replace(old, new)★Substring replacement.
s.startswith(p) · endswith(p)★Prefix/suffix test; also removeprefix/removesuffix (3.9+).
s.find(sub) → -1 s.index(sub) → raisesLocate a substring; in for a plain membership test.
s.upper · lower · title · casefoldCase transforms; casefold for i18n-safe comparison.
s.isdigit · isalpha · isidentifierContent predicates.
r"C:\path" b"bytes"Raw string (no escapes); bytes literal — str.encode() ↔ bytes.decode().
s.partition(sep) · s.rpartition(sep)Split once into (head, sep, tail) — the separator is kept.
s.splitlines() · s.zfill(6) · s.center(20)Split on line breaks; zero-pad; center within a width.
"{:.2f}".format(x) · format(v, ".2f")The str.format/format() siblings of f-strings — same spec mini-language.
06f-strings & Format Spec{value:fill align width .prec type}
f"{name} is {age}"★Interpolation, evaluated at runtime.
f"{price:.2f}"★2 decimal places.
f"{n:,}" f"{p:.1%}"Thousands separator; percentage.
f"{x:>10}" f"{x:^10}" f"{x:08d}"Right / center align to width 10; zero-pad.
f"{n:b}" f"{n:x}" f"{n:e}"Binary, hex, scientific.
f"{age=}"Debug spec — prints age=2.
f"{obj!r}"Use repr() instead of str().
f"{f"{inner}"}"3.12+Quote reuse & nesting allowed since the PEP 701 rewrite.
t"{user}" → Template3.14t-string sibling (PEP 750) — returns a Template, not a str. Safe HTML/SQL — see Part 0.
f"{value:{width}.{prec}f}"Nested fields — pull width/precision from variables.
f"{now:%Y-%m-%d %H:%M}"The spec is handed to the object — so date/time formatting works inline.
07Truthiness & Nonewhat counts as False
False · None · 0 · 0.0 · "" · [] · {} · set()★The falsy values; everything else is truthy.
if items: if not items:★Idiomatic emptiness check — not len(x) > 0.
x is None x is not None★Always identity, never == None.
value = x or defaultFallback idiom — beware: rejects 0/""/[] too.
def __bool__(self): …Custom truthiness; falls back to __len__.
08Bytes & Encodingtext ↔ raw octets
s.encode("utf-8") b.decode("utf-8")★The only bridge between str and bytes — cross it at the I/O boundary.
b"raw" bytes(5) bytearray(b"x")Immutable byte string; zero-filled; the mutable variant.
b.hex() bytes.fromhex("ff00")Hex round-trip — handy for hashes & wire dumps.
base64.b64encode(b) b64decode(s)ASCII-safe transport encoding (not encryption).
(1000).to_bytes(4, "big") int.from_bytes(b, "big")Integer ↔ fixed-width bytes with explicit endianness.
struct.pack(">IH", a, b) struct.unpack(...)Read/write C-style binary layouts.
memoryview(b)[1:4]Zero-copy view into a buffer — slice without duplicating.
open(p) for binary datacorruptsText mode re-encodes — use open(p, "rb"/"wb") for bytes.
Part II · Data Structures
09Listsordered, mutable · O(1) append
nums.append(x) · extend(iter)★Add one item / all items from an iterable.
nums.insert(i, x)Insert at index — O(n); prefer deque for front inserts.
nums.pop(i) · remove(x)★Pop by index (default last) / remove first matching value.
nums.sort(key=fn, reverse=True)★In-place, stable Timsort; sorted() returns a new list.
nums.index(x) · count(x)First position / occurrences.
x in numsMembership is O(n) — use a set for hot paths.
nums.copy() nums[:]Shallow copy — nested mutables still shared.
nums.reverse() · nums.clear()In-place reverse / empty. Both return None.
del nums[i] · del nums[1:3]Delete by index or slice.
10Tuples & Setsimmutable / unique · O(1) lookup
point = (3, 4) single = (1,)★Tuple — immutable; 1-item needs the trailing comma.
a = {1, 2, 3} set(iterable)★Set — unordered, unique, hashable elements only.
a | b a & b a - b a ^ b★Union · intersection · difference · symmetric diff.
a <= b a.isdisjoint(b)Subset test; no common elements.
a.add(x) · discard(x)discard never raises; remove does.
frozenset(a)Immutable set — usable as a dict key / set member.
{} → empty dict, not setgotchaEmpty set is set().
11Dictionarieskey → value · insertion-ordered
d.get(k, default)★Lookup without KeyError.
d.setdefault(k, [])Get, inserting a default if absent.
d.pop(k, default) · d.popitem()Remove by key / remove last-inserted pair.
d.items() · keys() · values()★Live views — reflect later changes.
d1 | d2 d1 |= d2★Merge (3.9+) — right side wins on conflicts.
dict(zip(keys, vals))Build from parallel sequences.
k in d★Membership checks keys, O(1).
for k in d: d.pop(k)runtime errorNever mutate a dict while iterating — iterate over list(d).
d.update(other) · d |= otherBulk-merge another mapping or keyword pairs.
dict.fromkeys(keys, 0)Build a dict with one default per key — don’t share a mutable here.
12Slice Notationseq[start:stop:step]
text[0] text[-1]★First; last (negative = from the end).
text[1:4]★Index 1, 2, 3 — stop is excluded.
text[:3] text[3:] text[:]From start; to end; shallow copy.
text[::2] text[::-1]★Every 2nd; reversed copy.
nums[2:5] = [a, b]Slice assignment can grow/shrink a list.
s = slice(1, 10, 2); seq[s]Slices are objects — reusable & nameable.
text[10:99999]Out-of-range slices never raise — they clamp.
13collectionsspecialized containers
Counter(words).most_common(3)★Frequency count + top-k in two calls.
defaultdict(list)★Auto-creates missing values — no key checks.
deque(xs, maxlen=n)★O(1) appendleft/popleft; bounded = rolling window.
namedtuple("Pt", "x y")Lightweight immutable record with named fields.
ChainMap(cli, env, defaults)Layered lookup across several dicts.
OrderedDict.move_to_end(k)Mostly legacy (dicts are ordered) — but this is LRU gold.
14heapq & bisectpriority & sorted access
heapq.heappush(h, x) · heappop(h)★Min-heap on a plain list — O(log n) each.
heapq.nsmallest(k, xs, key=fn)Top-k without a full sort (also nlargest).
heapq.heapify(xs)List → heap in place, O(n).
bisect.bisect_left(xs, x)Binary-search insertion point in a sorted list.
bisect.insort(xs, x)Insert keeping the list sorted.
heapq.merge(*sorted_iters)Lazily merge already-sorted inputs into one stream.
bisect.bisect_right(xs, x)Insertion point after equals; pair with bisect_left.
Part III · Control Flow & Functions
15Control Flowbranching
if … elif … else:★Colon + indentation define blocks (4 spaces, PEP 8).
x if cond else y★Conditional expression (ternary).
pass ...No-op placeholder; Ellipsis often marks stubs.
assert cond, "msg"Debug-time invariant — stripped under python -O, never for validation.
16Pattern Matchingmatch / case · 3.10+
match cmd:
case "quit" | "exit":★Literal + or-pattern; first match wins, no fallthrough.
case [x, y, *rest]:Sequence pattern — destructures like unpacking.
case {"type": t, **extra}:Mapping pattern — extra keys are ignored unless captured.
case Point(x=0, y=y):Class pattern — matches type + attributes.
case [x, *_] if x > 0:Guard — extra condition after the pattern.
case _:★Wildcard — the "default"; binds nothing.
case CONSTANT:gotchaBare names capture, not compare — use case Colors.RED: (dotted).
case str() | bytes():Class patterns match built-in types too — an isinstance-style branch.
case Point(x=0) | Point(y=0):Or-patterns compose with class patterns.
17Loopsiteration
for x in iterable:★Works on anything implementing the iteration protocol.
for i, x in enumerate(xs, start=1):★Index + value; never range(len(xs)).
for a, b in zip(xs, ys, strict=True):★Lockstep; strict (3.10+) raises on length mismatch.
while (chunk := f.read(8192)):Walrus loop — read-and-test in one line.
break continue★Exit loop; skip to next iteration.
for … else: while … else:else runs only if no break — "search failed" idiom.
for x in xs: xs.remove(x)gotchaMutating while iterating skips items — iterate a copy.
18Iterators & Protocoliter / next / StopIteration
it = iter(xs); next(it, default)★Manual iteration; default avoids StopIteration.
class R:
def __iter__(self): return self
def __next__(self): …A custom iterator implements both methods.
iter(fn, sentinel)2-arg form: call fn until it returns the sentinel.
list(it); list(it) → []gotchaIterators are single-use — the second pass is empty.
reversed(seq) sorted(iterable)Reverse iterator; new sorted list from anything iterable.
19Built-in Functionsalways available
len · min · max · sum · abs · round★Aggregates; min/max accept key= and default=.
any(it) all(it)★Short-circuit boolean reduction over an iterable.
map(fn, xs) filter(pred, xs)Lazy transform/filter; comprehensions usually read better.
isinstance(x, (int, float))★Type check (accepts a tuple); prefer over type(x) ==.
getattr(obj, "name", default)Dynamic attribute access; also setattr, hasattr.
vars(obj) · dir(obj) · id · type · reprIntrospection: attribute dict, names, identity, class, repr.
input("? ") print(*xs, sep=", ", end="")Console I/O; print takes sep/end/file/flush.
chr(65) · ord("A") · bin/oct/hex(n)Codepoint ↔ character; integer base representations.
sorted(it, key=…) · reversed(seq) · callable(x)New sorted list; reverse iterator; is-it-callable test.
eval(user_input) · exec(user_input)neverArbitrary-code execution — never on untrusted strings.
20Functionssignatures
def f(x, y=1):★Defaults are evaluated once, at def time.
def f(*args, **kwargs):★Collect extra positional / keyword args.
f(*nums, **opts)Unpack into a call.
def f(a, /, b, *, c):Positional-only ← / · * → keyword-only.
return a, bMultiple returns = one tuple, unpacked at the call site.
def f():
"""Docstring."""First-statement string → f.__doc__, help(f).
functions are objectsPass, return, store them — the basis of decorators.
21Closures & Scopeglobal · nonlocal · late binding
def outer():
n = 0
def inner(): return nClosure — inner captures the variable, not its value.
nonlocal nRebind a name in the enclosing function.
global counterRebind a module-level name (use sparingly).
[lambda: i for i in range(3)]late bindingAll see the final i — fix: lambda i=i: i.
x += 1 (no declaration)gotchaAssignment makes x local → UnboundLocalError.
22Lambdas & Functionalfunctools · operator
lambda x: x * 2★Single-expression anonymous function.
sorted(xs, key=lambda p: p[1])★The #1 lambda use case: sort keys.
operator.itemgetter(1) · attrgetter("name")Faster, named alternatives to key-lambdas.
functools.partial(f, arg1)Pre-fill some arguments; returns a new callable.
functools.reduce(fn, xs, init)Fold an iterable to one value.
23Comprehensionsbuild collections concisely
[x*x for x in xs if x > 0]★List comp — filter then transform, eager.
{k: v for k, v in pairs} {x for x in xs}★Dict / set comprehensions.
[y for row in grid for y in row]Nested = flatten; clauses read left-to-right like loops.
[a if a else 0 for a in xs]Ternary before for maps; if after filters.
3+ levels of nestingreadabilitySwitch to explicit loops — comprehensions aren't a flex.
[y for x in xs if (y := f(x)) is not None]Walrus in a comprehension — compute once, filter, and reuse the result.
24Generatorslazy · one value at a time
def gen():
yield x★Pauses at each yield; state is kept between calls.
(x*x for x in xs)★Generator expression — no brackets needed as a sole arg: sum(x*x for x in xs).
yield from subgen()Delegate to another generator.
g.send(value) · g.close()Push a value into a paused generator; finalize it.
lines → parse → filter → sinkChain generators into memory-flat pipelines.
len(gen) · gen[0]typeerrorGenerators have no length or indexing — they only iterate.
25Decoratorswrap behavior
@my_decorator
def f(): …★Sugar for f = my_decorator(f).
def deco(fn):
@functools.wraps(fn)
def wrap(*a, **kw): return fn(*a, **kw)
return wrapCanonical shape; wraps keeps name/docstring.
@retry(times=3)Parameterized decorator = factory returning a decorator.
@cache @staticmethod @propertyStack them — applied bottom-up.
26Context Managerswith · guaranteed cleanup
with open("f.txt") as f:★Cleanup runs even on exception.
with open(a) as f, open(b) as g:Multiple managers; parenthesized groups OK (3.10+).
__enter__ / __exit__The protocol; __exit__ returning True suppresses the exception.
@contextmanager
def cm():
yield resourceGenerator → context manager; wrap the yield in try/finally.
contextlib.suppress(FileNotFoundError)Cleanly ignore a specific exception.
with contextlib.ExitStack() as stack:Enter a dynamic number of managers; unwinds them all in reverse.
@asynccontextmanager async def cm(): yieldAsync context managers — drive them with async with.
27itertoolsiterator algebra
chain(a, b) chain.from_iterable(xss)★Concatenate iterables lazily; flatten one level.
product(a, b) · permutations · combinationsCartesian product & combinatorics.
islice(gen, 10)★Slice an iterator — take the first n lazily.
groupby(xs, key=fn)Groups consecutive matches — sort by the key first.
accumulate(xs)Running totals (or any binary fn).
count(10) · cycle(xs) · repeat(x, n)Infinite/repeating iterators — pair with islice.
pairwise(xs) batched(xs, n)3.10 / 3.12+Adjacent pairs; fixed-size chunks.
takewhile(p, xs) · dropwhile(p, xs) · filterfalse(p, xs)Condition-driven slicing of a stream.
zip_longest(a, b, fillvalue=0) · starmap(f, pairs)Pad-zip unequal lengths; map over pre-zipped argument tuples.
a, b = tee(it, 2)Fork one iterator into independent copies (buffers what it must).
28functoolsfunction tools
@functools.cache★Unbounded memoization (3.9+); @lru_cache(maxsize=128) to bound it.
@cached_propertyCompute once per instance, then behave as an attribute.
@singledispatchFunction overloading by the type of the first argument.
@total_orderingDefine __eq__ + one comparison; get the rest free.
partial · reduce · wrapsSee cards 21 & 24.
Part IV · Object-Oriented Python
29Classes & Instancesthe basics
class Dog:
species = "canine"
def __init__(self, name):
self.name = name★Class attribute (shared) vs. instance attribute.
@property
def area(self): …★Computed read-only attribute; add @area.setter for writes.
@classmethod
def from_json(cls, s): …Alternative constructors; receives the class.
@staticmethodNamespaced plain function; no implicit argument.
__slots__ = ("x", "y")Fixed attributes — less memory, no __dict__, faster access.
class C: items = []sharedMutable class attribute is shared by all instances.
30Magic (Dunder) Methodsoperator overloading
__repr__★Unambiguous, for developers; aim for eval-able output.
__str__Readable, for users; falls back to __repr__.
__eq__ + __hash__★Define together — objects equal ⇒ same hash; defining __eq__ alone makes the class unhashable.
__lt__ · __le__ · __gt__ · __ge__Ordering (or use @total_ordering).
__len__ · __getitem__ · __contains__Make it sized, indexable, and in-testable.
__iter__ · __next__The iteration protocol (card 17).
__call__Make instances callable like functions.
__add__ · __mul__ · __radd__ …Arithmetic operators; r-versions handle other + self.
__enter__ · __exit__Context-manager protocol (card 25).
__format__(self, spec)Drives format(obj, spec) and f-string/t-string specs.
__getattr__ · __setattr__ · __delattr__Intercept attribute access — proxies, lazy fields, defaults.
__init_subclass__(cls) · __set_name__(self, owner, name)Hooks that fire when a subclass or descriptor is defined.
31Inheritance & MROsuper · abc · mixins
class Puppy(Dog):
def __init__(self):
super().__init__()★super() follows the MRO, not just "the parent".
C.__mro__ C.mro()Method Resolution Order — C3 linearization, left-to-right.
class Base(ABC):
@abstractmethod
def run(self): …Abstract base class — can't instantiate until implemented.
class JSONMixin: …Mixins add one capability; list them before the base.
isinstance(x, Base) issubclass(C, Base)Runtime type relationships.
deep inheritance treesdesignPrefer composition — "has-a" beats "is-a" beyond ~2 levels.
32Dataclassesless boilerplate
@dataclass
class Point:
x: int
y: int = 0★Auto __init__, __repr__, __eq__; defaults after non-defaults.
@dataclass(frozen=True, slots=True)Immutable + memory-efficient (slots: 3.10+).
field(default_factory=list)★The only safe mutable default.
field(repr=False, compare=False)Exclude a field from repr/comparisons.
@dataclass(kw_only=True)3.10+Force keyword-only construction.
asdict(obj) astuple(obj)Recursive conversion for serialization.
def __post_init__(self): …Runs after the generated __init__ — validation & derived fields.
replace(obj, x=9) · fields(obj)Copy-with-changes (frozen-friendly); introspect declared fields.
33Enum · NamedTuple · TypedDictstructured alternatives
class Color(Enum):
RED = 1★Named constants; Color.RED.name / .value; also auto().
class Color(StrEnum): …3.11+Members are usable directly as strings.
class Pt(NamedTuple):
x: int
y: intTyped, immutable record — tuple behavior + names.
class User(TypedDict):
name: str
age: intType-checked dict shape — still a plain dict at runtime.
NotRequired[str]3.11+Optional TypedDict key.
class Perm(Flag): R = auto(); W = auto()Bit-flag enum — combine with |; @unique bans aliases; IntEnum compares as int.
ReadOnly[int] · Required[str]3.13 / 3.11+TypedDict key qualifiers.
Part V · Errors, I/O & Data
34Exceptionsfail gracefully
try: … except ValueError as e:★Catch the most specific type that applies.
except (TypeError, KeyError):Multiple types in one clause.
else: … finally: …else if no error; finally always.
raise ValueError("msg") from err★Chain causes — keeps the original traceback context.
raiseBare re-raise inside except preserves the traceback.
e.add_note("context")3.11+Attach extra info to an in-flight exception.
except* ValueError as eg:3.11+ExceptionGroup handling — one type from a batch.
except TimeoutError, ConnectionError:3.14PEP 758 — brackets optional when there's no as clause.
return/break/continue in finally3.14PEP 765 — now a SyntaxWarning; it can silently swallow exceptions.
except: passneverSwallows everything, incl. KeyboardInterrupt. At minimum: except Exception + log.
class ConfigError(Exception): ...Custom exceptions subclass Exception (never BaseException).
raise ValueError("bad") from NoneSuppress a noisy chained cause when it adds nothing.
35File I/O & Pathlibread / write / paths
with open("f.txt", encoding="utf-8") as f:★Always state encoding — the platform default varies.
for line in f:★Line-by-line — memory-flat for huge files.
modes: r · w · a · x · rb · wbRead; overwrite; append; create-only (fails if exists); binary.
p = Path("data") / "file.txt"★/ operator joins paths, cross-platform.
p.read_text() p.write_text(s)★Whole-file one-liners with auto open/close.
p.glob("**/*.py") p.exists() p.stem · p.suffixRecursive matching; checks; name parts.
p.mkdir(parents=True, exist_ok=True)Safe directory creation.
p.copy(dst) p.move(dst)3.14New in 3.14 — copy / move a file straight from a Path.
p.parent · p.name · p.stem · p.with_suffix(".md")Path parts & transforms — all pure, no disk access.
p.iterdir() · p.rglob("*.txt")List a directory; recursive glob. shutil for tree copy/rm.
36JSON & Serializationjson · csv · pickle
json.dumps(obj, indent=2)★Object → JSON string; dump writes to a file.
json.loads(s) json.load(f)★Parse from string / file.
json.dumps(o, default=str)Escape hatch for dates/Decimals; keys become strings.
csv.DictReader(f) csv.DictWriterRows as dicts; open the file with newline="".
pickle.load(untrusted)securityUnpickling can execute arbitrary code — trusted data only.
tomllib.load(f) # "rb"3.11+Read TOML (e.g. pyproject.toml) from the stdlib — read-only, binary mode.
json.dumps(asdict(obj), indent=2)Dataclass → dict → JSON in one line.
37Regex — re modulepattern matching on text
re.search(pat, s)★First match anywhere → Match or None.
re.match · re.fullmatchAnchored at start / must span the whole string.
re.findall(pat, s) · finditer★All matches as strings / as Match objects (lazy).
re.sub(pat, repl, s)★Replace; repl may be a function.
pat = re.compile(r"(?P<year>\d{4})")Precompile in loops; named groups → m.group("year").
m.group(1) · m.groups() · m.span()Capture access; match position.
.* (greedy)careGreedy by default; use .*? for lazy; always raw strings r"…".
re.split(r"\s+", s) · re.subn(pat, r, s)Split on a pattern; subn also returns a replacement count.
re.compile(pat, re.I | re.M | re.X)Flags: ignore-case, multiline anchors, verbose (commented) patterns.
38datetimedates · times · zones
datetime.now(tz=timezone.utc)★Aware "now" — prefer over naive utcnow() (deprecated).
date(2026, 7, 3) timedelta(days=7)Date literal; durations support + - * comparisons.
dt.strftime("%Y-%m-%d %H:%M")★Format → string.
datetime.strptime(s, "%d/%m/%Y")Parse ← string.
datetime.fromisoformat(s) · dt.isoformat()★ISO-8601 round-trip — the sane default format.
ZoneInfo("Asia/Kolkata")IANA timezones in the stdlib (3.9+) — no pytz needed.
naive vs awaregotchaComparing/mixing them raises — store UTC, convert at the edges.
date.today() · datetime.now(tz)Today’s date; timezone-aware now.
dt.astimezone(ZoneInfo("Asia/Tokyo"))Convert an aware datetime between zones.
dt.timestamp() ↔ datetime.fromtimestamp(t, tz)Round-trip through POSIX epoch seconds.
Part VI · Typing, Async & Concurrency
39Type Hints · basicsannotate for tooling
def f(x: int) -> bool:★Checked by mypy/pyright — never enforced at runtime.
list[int] dict[str, int] tuple[int, ...]★Built-in generics (3.9+) — no typing.List.
int | None★Union syntax (3.10+) — replaces Optional[int].
Callable[[int], str] Iterable[int]Function & ABC types — prefer collections.abc.
x: Final = 3 MAX: Final[int] = 9Write-once constant the checker guards.
def f(a: Node) -> Node: # no quotes3.14PEP 649 — annotations lazily evaluated; forward refs need no strings.
annotationlib.get_annotations(f, format=…)3.14Introspect as VALUE / FORWARDREF / STRING.
40Type Hints · advancedgenerics · narrowing · escape hatches
def first[T](xs: list[T]) -> T:3.12+PEP 695 inline generics — no TypeVar import.
type Vector = list[float]3.12+The type statement — a true, lazy alias.
class Sized(Protocol):
def __len__(self) -> int: …Structural (“duck”) typing — no inheritance.
@overloadDeclare several precise signatures for one implementation.
@override3.12+Checker verifies it really overrides a parent.
Self3.11+Return own (sub)class from a method — no TypeVar.
Literal["r", "w"] · Annotated[int, "unit"]Exact allowed values; attach metadata to a type.
cast(T, x) · if TYPE_CHECKING: · reveal_type(x)Escape hatch; import only for the checker; ask what it inferred.
assert_never(x) · Never · TypeIs[T]3.11 / 3.13Exhaustiveness; the empty type; user-defined narrowing.
P = ParamSpec("P") · Concatenate[int, P]3.10+Preserve a wrapped callable’s signature through a decorator.
41Async / Awaitcooperative concurrency
async def fetch(): …
await fetch()★Coroutine — runs only when awaited or scheduled.
asyncio.run(main())★The single entry point — starts/stops the event loop.
async for x in stream: async with conn:Async iteration & context managers.
$ python -m asyncio ps PID pstree PID3.14Inspect the running task tree of a live async process.
time.sleep(1) in async codeblocks loopBlocks everything — use await asyncio.sleep(1).
fetch() without awaitno-opMakes a coroutine object that never runs (RuntimeWarning).
42asyncio · patternsorchestrate & bridge
await asyncio.gather(a(), b())★Run coroutines concurrently; results in order.
async with asyncio.TaskGroup() as tg:
tg.create_task(work())3.11+Structured concurrency — waits for all, cancels siblings on failure.
async with asyncio.timeout(5): …3.11+Deadline over a block; wait_for(coro, t) for one await.
await asyncio.to_thread(blocking_fn, *args)★Push a blocking call off the loop — the simple sync bridge.
q = asyncio.Queue(); await q.put(x); await q.get()Backpressure-friendly producer/consumer.
for fut in asyncio.as_completed(tasks): await futConsume results in completion order.
asyncio.Lock() · Event() · Semaphore(n)Async-native primitives — never the threading ones in a loop.
await loop.run_in_executor(pool, fn, *a)Offload to a thread/process pool when to_thread isn’t enough.
43Threads, Processes & the GILparallelism
with ThreadPoolExecutor() as ex:
ex.map(fetch, urls)★Threads — right for I/O-bound work.
with ProcessPoolExecutor() as ex:★Processes — right for CPU-bound work; sidesteps the GIL.
fut = ex.submit(fn, x); fut.result()Single task → Future; as_completed() to stream results.
threading.Lock() → with lock:Guard shared mutable state.
GILOne thread executes Python bytecode at a time; I/O & C extensions release it.
python3.14t sys._is_gil_enabled()3.14PEP 779 — free-threaded build now officially supported (opt-in; ~5–10% single-thread cost).
from concurrent import interpreters3.14PEP 734 subinterpreters — process-like isolation, thread-like cost; pair with InterpreterPoolExecutor.
processes share nothingnoteArguments/results are pickled — big objects are expensive.
from queue import QueueThread-safe FIFO — the right channel between worker threads.
threading.Event() · Semaphore(n) · Barrier(n)Coordinate threads without busy-waiting.
Part VII · Project, Tooling & Quality
44Modules, Packages & venvorganize & isolate
import module from pkg import x as y★Modules run once; later imports hit the cache.
pkg/__init__.pyMarks a package; runs on first import; controls from pkg import * via __all__.
python -m venv .venv★Isolated environment per project.
source .venv/bin/activate★Windows Git-Bash: source .venv/Scripts/activate.
pip install pkg pip freeze > requirements.txt★Install; snapshot exact versions.
pyproject.tomlModern single config for build, deps & tools (PEP 621).
from module import *avoidPollutes the namespace; hides where names come from.
uv venv · uv pip install · uv runuv — a fast, drop-in replacement for venv+pip (external, but now ubiquitous).
pip install -e .Editable install — your source changes take effect without reinstalling.
45CLI, Env & OSscripts that take input
sys.argvRaw arguments; argv[0] is the script name.
p = argparse.ArgumentParser()
p.add_argument("--n", type=int)★Declarative CLI with auto --help.
os.environ.get("API_KEY")★Environment variables — never hardcode secrets.
subprocess.run([…], capture_output=True, check=True)Run external commands; list form avoids shell injection.
sys.exit(1)Non-zero = failure, for shells & CI.
46Loggingprint() for production
logging.basicConfig(level=logging.INFO)★One-line setup in the entry script only.
log = logging.getLogger(__name__)★Per-module logger — the standard pattern.
log.debug · info · warning · error · criticalSeverity ladder; default threshold is WARNING.
log.info("user %s", uid)Lazy %-formatting — skipped if the level is off.
log.exception("failed")Inside except: message + full traceback.
logging.config.dictConfig(CONFIG)Configure handlers, formatters & levels declaratively from a dict.
RotatingFileHandler("app.log", maxBytes=…, backupCount=…)Size-based rotation; TimedRotatingFileHandler for time-based.
47Testingpytest-first
def test_add():
assert add(2, 2) == 4★pytest: plain functions + plain asserts.
with pytest.raises(ValueError):★Assert an exception is raised.
@pytest.mark.parametrize("a,b", [(1,2),(3,4)])One test, many cases.
@pytest.fixture
def db(): yield connReusable setup/teardown via injection.
unittest.mock.patch("mod.api_call")Replace collaborators; patch where it's used, not defined.
pytest -x -k "pattern" --lfStop at first fail; filter by name; rerun last failures.
def test_x(tmp_path, monkeypatch, capsys): …Built-in fixtures: temp dir, patching, captured output — injected by name.
assert value == pytest.approx(0.3)Float-safe equality; put shared fixtures in conftest.py.
48Debugging & Profilingfind it, then time it
breakpoint()★Drop into pdb right here (n=next, s=step, c=continue, p expr).
python -m pdb script.pyPost-mortem debugging from the start.
timeit.timeit(fn, number=10_000)Micro-benchmarks; or python -m timeit "expr".
python -m cProfile -s cumtime script.pyWhere the time actually goes.
sys.getsizeof(obj) tracemallocObject size; allocation tracking.
warnings.warn("deprecated", DeprecationWarning)Soft signals; -W error makes them fatal in CI.
t0 = time.perf_counter(); … ; time.perf_counter() - t0High-resolution wall-clock timing for ad-hoc measurement.
dis.dis(fn) · gc.collect() · faulthandler.enable()Disassemble bytecode; force a GC pass; dump tracebacks on hard crashes.
49Common Pitfallshandle with care
def f(x=[]):gotchaMutable default shared across calls — use x=None sentinel.
rows = [[0]*3]*3gotchaThree references to one inner list — use a comprehension.
a is b (for values)gotchaSmall-int/string caching makes it "work" sometimes — use ==.
copy.copy(x) (nested)shallowInner mutables still shared — deepcopy when nesting.
json.py · random.py as filenamesshadowingYour file shadows the stdlib module — imports break mysteriously.
"a" + 1typeerrorNo implicit coercion — convert explicitly: "a" + str(1).
tuple1 += (x,) inside a listsubtleAugmented ops on immutables rebind — surprising inside containers.
xs = mylist.sort()returns NoneIn-place methods return None — use sorted() for a new list.
for i in range(len(xs)): use xs[i]smellUnpythonic — iterate enumerate(xs) for index + value.
Worth memorizing
is ≠ ==is checks identity; == checks value — is only for None
mutable defaultsevaluated once at def time — use None as a sentinel
copy ≠ deepcopycopy() is one level deep; nested mutables still shared
list * n[[0]*3]*3 repeats one inner list, not three
global vs nonlocalglobal → module scope; nonlocal → enclosing function
param orderpositional-only / normal *args keyword-only **kwargs
iterators die oncea consumed generator/iterator yields nothing on pass two
set/dict need hashablekeys & members must be immutable-ish (tuple ✓, list ✗)
EAFP over LBYLtry/except beats pre-checking in idiomatic Python
await ≠ callcalling a coroutine does nothing until awaited/scheduled
GIL rule of thumbI/O-bound → asyncio/threads · CPU-bound → processes
case NAME capturesbare names in match bind — dotted names compare
encoding="utf-8"always pass it to open(); platform defaults differ
except Exceptionnever bare except: — it eats KeyboardInterrupt too
sorted() is stableequal keys keep order — chain sorts from minor to major key
f"{x=}"the fastest debug print you'll ever type
t"…" ≠ f"…"t-string yields a Template you can sanitize; f-string yields a finished str
no-GIL is opt-in3.14 free-threading is a separate python3.14t build, not the default
bytes ≠ strencode/decode at the I/O boundary; never concatenate the two
in-place → Nonesort()/reverse()/append() mutate and return None, not the object