PythonCheatsheet

Python Syntax Cheatsheet📋

The Python syntax you actually reach for daily, organized so you stop scrolling through Stack Overflow. Covers core constructs through modern 3.13+ additions. Each entry has copy-pasteable code and the kind of tips that save you a debugging session.

Quick Reference

NameSyntaxDescription
List Comprehension[expr for x in iterable if cond]Build a list in one expression. Add if-clause to filter, nest for-clauses to flatten.
Dict Comprehension{k: v for k, v in iterable}Same idea as list comps, but produces a dict. Handy for inverting or filtering mappings.
F-Stringsf"{expr}" / f"{val:.2f}" / f"{x=}"Inline expressions in strings. Supports format specs and = for debug printing.
Walrus Operatorif (n := len(items)) > 10:Assigns and returns in one shot. Avoids redundant calls in conditions and comprehensions.
match/casematch value: case pattern: ...Structural pattern matching (3.10+). Destructures sequences, mappings, and class attributes.
Lambdalambda args: expressionAnonymous single-expression function. Use for short callbacks, not complex logic.
*args / **kwargsdef f(*args, **kwargs):Variadic positional and keyword arguments. args is a tuple, kwargs is a dict.
Unpackingfirst, *rest = iterableDestructure sequences. The starred target collects leftover items into a list.
Decorators@decorator\ndef func(): ...Wrap a function or class. Decorator receives the target and returns a replacement.
Generatorsdef gen(): yield valueLazy iterator. Yields values one at a time instead of building a full list in memory.
Context Managerswith open(path) as f:Guarantees cleanup via __enter__/__exit__. Use contextlib for lightweight custom managers.
Type Hintsdef f(x: int) -> str:Static annotations. No runtime enforcement, but mypy/pyright catch bugs before you ship.
@dataclass@dataclass\nclass Point: x: float; y: floatAuto-generates __init__, __repr__, __eq__. Add frozen=True for immutability.
__slots____slots__ = ('x', 'y')Restricts instance attributes. Saves memory and prevents typo-based attribute creation.
ExceptionGroupexcept* TypeError as eg:Catch multiple concurrent exceptions (3.11+). Each except* handles a subset of the group.

Data Structures

List Comprehensions & Generator Expressions

[expr for x in iterable if condition]

List comps replace map/filter for most cases. Generator expressions (round parens) are lazy and use constant memory. Use set or dict comps when you need those types directly.

python

Tips

  • Prefer generator expressions inside sum(), any(), all(), and min()/max() to avoid allocating a throwaway list
  • If the comprehension body exceeds one line of logic, switch to a for-loop. Readability counts.
  • Walrus operator inside a comp lets you filter and transform in one pass: [y for x in data if (y := transform(x)) is not None]

Dictionary Operations

d.get(key, default) / d | other / d.setdefault(k, v)

Dicts are the backbone of Python data handling. The | merge operator (3.9+) replaced {**a, **b}. defaultdict and Counter from collections handle 90% of grouping and counting tasks.

python

Tips

  • Use dict.get() instead of catching KeyError. It is cleaner and avoids the try/except overhead.
  • dict.setdefault() is underused: it returns the existing value if the key exists, or sets and returns the default
  • For nested dicts, consider dict.get(k1, {}).get(k2, default) or just use a dataclass

Unpacking & Starred Expressions

a, *rest, z = iterable / {**d1, **d2}

Unpacking eliminates index-based access and makes code self-documenting. Starred expressions handle variable-length sequences. Double-star unpacking merges dicts (though | is preferred in 3.9+).

python

Tips

  • Use _ for values you want to discard: _, _, z = get_coordinates()
  • Starred unpacking works in for-loops too: for first, *rest in rows
  • You can only have one starred variable per assignment level

Control Flow

match/case (Structural Pattern Matching)

match subject:\n case pattern [if guard]: ...

Added in 3.10. Goes far beyond a switch statement: it destructures sequences, mappings, and objects. Guards (if-clauses) add runtime conditions to patterns. The _ wildcard matches anything.

python

Tips

  • Patterns are matched top to bottom; put specific cases before general ones
  • Use | to combine patterns: case 'y' | 'yes' | 'Y':
  • Capture variables bind to the matched value: case [x, y] binds x and y to the first and second elements
  • Class patterns require keyword args by default: case Point(x=x, y=y)

Walrus Operator (:= Assignment Expression)

(name := expression)

The walrus operator assigns a value inside an expression, so you can test and capture in one step. Most useful in while-loops, if-conditions, and comprehension filters where you would otherwise need a throwaway variable or redundant call.

python

Tips

  • Always wrap in parentheses for clarity, especially in if-statements
  • Do not overuse it. If the assignment makes the line hard to read, use a separate line.
  • Cannot be used at the top level of an expression statement: x := 5 is a SyntaxError, use x = 5

for/else and while/else

for x in seq:\n ...\nelse:\n # ran if no break

The else clause on a loop runs only if the loop completed without hitting a break. It is a clean way to handle the "searched but did not find" pattern without extra boolean flags.

python

Tips

  • Think of it as 'nobreak' rather than 'else'. The name is confusing, but the pattern is clean.
  • If you find yourself adding a found = False flag, for/else is probably what you want
  • Works identically on while loops

Functions

*args, **kwargs, and Keyword-Only Arguments

def f(pos, /, normal, *, kw_only, **kwargs):

Python gives precise control over how arguments are passed. Positional-only (/) prevents callers from using keyword syntax, which lets you rename parameters later without breaking the API. Keyword-only (*) forces clarity at the call site.

python

Tips

  • Use keyword-only args for flags and options: def fetch(url, *, verify_ssl=True)
  • Positional-only params (3.8+) protect your API surface. The stdlib uses them heavily.
  • *args must come before **kwargs in the signature

Decorators

@decorator\ndef func(): ...

Decorators are just higher-order functions. Always use @functools.wraps to preserve the original function's name, docstring, and signature. Decorators with arguments need an extra nesting level.

python

Tips

  • Always use @functools.wraps(func) on the wrapper. Without it, debugging and introspection break.
  • For simple cases, consider a class-based decorator with __call__
  • Stack multiple decorators: they apply bottom-up, so the outermost decorator runs first at call time

Generators & yield

def gen(): yield value / yield from iterable

Generators produce values lazily: they only compute the next item when asked. This makes them ideal for large datasets, infinite sequences, and pipeline-style data processing. yield from delegates iteration to another generator or iterable.

python

Tips

  • A generator expression (x for x in ...) is the inline equivalent of a generator function
  • Generators are single-use: once exhausted, they produce nothing. Create a new one to re-iterate.
  • Use itertools (chain, islice, groupby, product) to compose generators without writing custom code

Lambda & Higher-Order Functions

lambda args: expression

Lambdas are anonymous single-expression functions. They shine as sort keys, callback arguments, and simple transforms. For anything more than one expression, use a named function.

python

Tips

  • If you find yourself assigning a lambda to a variable, just write a def instead. PEP 8 says so.
  • Lambdas cannot contain statements (no assignments, no if/else blocks without ternary)
  • Use operator.itemgetter() or operator.attrgetter() instead of lambda for attribute/item access in sort keys

Classes

@dataclass

@dataclass(frozen=False, slots=False, kw_only=False)

Dataclasses auto-generate __init__, __repr__, __eq__, and optionally __hash__. frozen=True gives you immutability. slots=True (3.10+) reduces memory and prevents accidental attribute creation. kw_only=True forces callers to be explicit.

python

Tips

  • Never use a mutable default directly: use field(default_factory=list) instead of tags: list = []
  • frozen + slots is the sweet spot for config objects and value types
  • For validation on creation, use __post_init__ to run checks after auto-generated __init__

__slots__ and Memory Optimization

__slots__ = ('attr1', 'attr2')

Slots replace the per-instance __dict__ with a fixed set of attribute descriptors. This saves ~40-60 bytes per instance and speeds up attribute access. In modern Python, @dataclass(slots=True) is the easiest way to enable this.

python

Tips

  • Do not add __dict__ to __slots__ unless you specifically need dynamic attributes on some instances
  • Every class in the inheritance chain must define __slots__ for the optimization to work
  • Slots prevent monkey-patching and dynamic attribute addition, which is usually what you want for data objects

Context Managers

with resource as var: / @contextmanager

Context managers guarantee cleanup via the with statement. Use @contextmanager from contextlib instead of writing __enter__/__exit__ by hand. The parenthesized form (3.10+) handles multiple managers cleanly.

python

Tips

  • contextlib.suppress(ExceptionType) is a one-liner to ignore specific exceptions
  • contextlib.closing() wraps any object with a .close() method into a context manager
  • Async context managers use async with and are essential for managing network connections

Error Handling

try/except/else/finally

try: ... except Type as e: ... else: ... finally: ...

The else clause runs only if try succeeded, keeping the 'happy path' out of the try block. finally always runs regardless. Use 'from' for explicit exception chaining so the traceback shows the causal relationship.

python

Tips

  • Never use bare except: or except Exception: without re-raising. You will swallow KeyboardInterrupt and SystemExit.
  • Put as little code as possible in the try block. Only wrap the line that can actually raise.
  • 'raise' without arguments re-raises the current exception with the original traceback intact

Exception Groups (3.11+)

raise ExceptionGroup(msg, [e1, e2]) / except* Type:

Exception groups let you raise and handle multiple exceptions simultaneously. The except* syntax matches subsets of the group by type. Essential for concurrent code where multiple tasks can fail at once.

python

Tips

  • except* always receives an ExceptionGroup, even if only one exception matched
  • You cannot mix except and except* in the same try block
  • Each except* clause handles a disjoint subset; unmatched exceptions propagate automatically

Modern Python (3.10+)

Type Hints & Union Syntax

x: int | str / list[int] / dict[str, Any]

Python's type system has matured rapidly. The X | Y union syntax (3.10+) replaced typing.Union. Built-in generics (3.9+) eliminated imports for list, dict, tuple, and set. The type statement (3.12+) replaced TypeAlias.

python

Tips

  • Use X | None instead of Optional[X]. It is clearer and more explicit.
  • Run mypy --strict or pyright on CI. Type hints only help if something checks them.
  • Use TypedDict for dicts with known string keys: class Config(TypedDict): host: str; port: int

Parenthesized Context Managers (3.10+)

with (\n cm1() as a,\n cm2() as b,\n):

The parenthesized with statement (3.10+) makes multiple context managers readable without backslash continuation. Supports trailing commas for easy git diffs when adding or removing managers.

python

Tips

  • The trailing comma is optional but recommended for multi-line form
  • This was actually a side effect of the new PEG parser introduced in 3.9
  • Combine with contextlib.ExitStack when the number of context managers is dynamic

F-String Improvements (3.12+)

f"{data["key"]}" / f"{obj!r:>20}"

F-strings got major quality-of-life upgrades in 3.12: you can nest the same quote type inside expressions and break expressions across multiple lines. The = specifier (3.8+) is still the fastest debug print trick around.

python

Tips

  • f"{x=}" is shorthand for f"x={x!r}". It prints the expression and its repr.
  • Use !s, !r, !a for str(), repr(), ascii() conversion before format specs
  • F-strings are evaluated at runtime. Do not put expensive calls inside them in hot loops.

type Statement & TypeVar Syntax (3.12+)

type Alias = ... / def f[T](x: T) -> T:

The type statement (3.12+) declares type aliases without importing TypeAlias. The bracket syntax [T] on functions and classes replaces explicit TypeVar declarations. Both reduce boilerplate and improve readability.

python

Tips

  • type aliases support forward references naturally, unlike the old TypeAlias form
  • The bracket syntax supports bounds (T: SomeBase) and constraints (T: (int, str))
  • These features require Python 3.12+. If you support older versions, stick with typing imports.

Common Patterns

Data processing pipeline

python

Combines dataclasses for typed records, a context manager for file handling, list comprehensions for parsing, and generator expressions for aggregation. This is the kind of pipeline you write every week in data-heavy Python.

Retry decorator for API requests

python

A production-grade retry decorator with exponential backoff. Uses modern generic syntax [T], functools.wraps for introspection, and parameterized exception types so you only retry on recoverable failures.

Config loader with match/case

python

Uses match/case to validate and destructure a TOML config in one step. The pattern enforces required keys and types at parse time. Combined with frozen dataclasses for an immutable config object.

Watch Out For

Mutable default arguments are shared across calls: def append_to(item, target=[]) reuses the same list object every time

Use None as the default and create a new object inside the function: def append_to(item, target=None): target = target if target is not None else []. Default argument values are evaluated once at function definition time, not at each call.

Late binding closures in loops: lambdas and inner functions capture the variable, not its current value, so [lambda: i for i in range(3)] all return 2

Bind the value with a default argument: [lambda i=i: i for i in range(3)]. The default argument is evaluated at definition time, freezing the current value of i into each lambda.

is vs ==: 'is' checks identity (same object in memory), '==' checks equality. Small int caching (-5 to 256) makes 'is' seem to work for ints, but it breaks for larger values

Always use == for value comparison. Use 'is' only for singletons: None, True, False. Write 'if x is None', never 'if x == None'.

Modifying a list while iterating over it causes skipped elements or IndexError: for item in my_list: if bad(item): my_list.remove(item)

Iterate over a copy (for item in my_list[:]:) or, better, use a list comprehension to build a new list: my_list = [item for item in my_list if not bad(item)]. The comprehension approach is both safer and more Pythonic.

Integer division with / returns a float, even for exact results: 10 / 2 returns 5.0, not 5. This can cause subtle type bugs in dict keys and equality checks.

Use // for integer division when you want an int result: 10 // 2 returns 5. Be aware that // with negative numbers rounds toward negative infinity: -7 // 2 is -4, not -3.

Master Python with Stanza

Go beyond the cheatsheet with hands-on lessons and challenges.

Dive Deeper