Pythonic Patterns Basics
10 examples to get you started with Pythonic Patterns - 7 basic and 3 intermediate.
Search across all documentation pages
10 examples to get you started with Pythonic Patterns - 7 basic and 3 intermediate.
import this for the Zen of Python - readability counts.Try the operation; handle KeyError for missing keys.
def get_score(scores: dict[str, int], player: str) -> int:
try:
return scores[player]
except KeyError:
return 0.get() when a dict default is enough.Related: EAFP vs LBYL - when to guard instead
Guarantee cleanup with with.
from pathlib import Path
path = Path("notes.txt")
path.write_text("hello", encoding="utf-8")
with path.open(encoding="utf-8") as f:
print(f.read())pathlib over manual path strings.encoding for text files on all platforms.Related: Context Managers as a Pattern - custom managers
Replace if/elif with a registry of callables.
FORMATTERS = {
"json": lambda data: str(data),
"upper": lambda data: str(data).upper(),
}
def format_value(kind: str, data) -> str:
return FORMATTERS[kind](data)Related: The Strategy & Dispatch Patterns - singledispatch too
Hide concrete class choice behind one constructor function.
from dataclasses import dataclass
@dataclass
class ConsoleLogger:
def info(self, msg: str) -> None:
print(msg)
def logger_from_env(debug: bool) -> ConsoleLogger:
return ConsoleLogger()Related: Factory & Builder Patterns - builders for complex objects
Decouple publishers and subscribers with topics.
from collections import defaultdict
bus: dict[str, list] = defaultdict(list)
bus["user.created"].append(lambda e, p: print("audit", p))
bus["user.created"][0]("user.created", {"id": 1})Related: Observer & Pub/Sub - production-shaped bus
Pass dependencies explicitly for testability.
class Greeter:
def __init__(self, salutation: str) -> None:
self._salutation = salutation
def greet(self, name: str) -> str:
return f"{self._salutation}, {name}!"Related: Dependency Injection in Python - protocols and wiring
Distinguish "not passed" from None.
UNSET = object()
def connect(timeout=UNSET):
if timeout is UNSET:
timeout = 30.0
return timeoutis, not ==.None is valid data.model_fields_set instead for APIs.Related: Null Object & Sentinels - null objects
Add type-specific serializers without editing the core function.
from functools import singledispatch
@singledispatch
def to_repr(obj) -> str:
raise TypeError(type(obj))
@to_repr.register(int)
def _(n: int) -> str:
return f"int:{n}"
print(to_repr(42))TypeError for unsupported types.Related: The Strategy & Dispatch Patterns - full comparison
Assemble a config object step-by-step; validate at build().
class QueryBuilder:
def __init__(self) -> None:
self._filters: list[str] = []
def where(self, clause: str) -> "QueryBuilder":
self._filters.append(clause)
return self
def build(self) -> str:
if not self._filters:
raise ValueError("need at least one filter")
return " AND ".join(self._filters)
sql = QueryBuilder().where("id > 0").where("active").build()
print(sql)self for chaining; validate invariants once in build().Related: Factory & Builder Patterns - factory vs builder
Skip branching when logging is disabled.
class NullLogger:
def info(self, msg: str) -> None:
pass
def job(logger=NullLogger()):
logger.info("run")Related: Common Anti-Patterns - mutable defaults and god objects
Stack versions: This page was written for Python 3.14.0 (stable 3.14, maintenance 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, and uv 0.6+.
Reviewed by Chris St. John·Last updated Jul 19, 2026