Frontend Labs
⋯

71. How not to write Python: an anti-pattern catalog

Anti-pattern Why it hurts Instead
def f(x=[]) shared mutable default (Ch 2.6) x=None, create inside
except: / except Exception: pass hides bugs, eats Ctrl-C catch specific exceptions; log with traceback at boundaries (Ch 7.2)
from module import * unknown names, shadowing explicit imports
if x == None, if len(xs) == 0 unidiomatic, == can be overridden if x is None, if not xs
for i in range(len(xs)): xs[i] noisy, error-prone for x in xs, enumerate, zip
x in big_list in a loop O(n) each time a set (Ch 6.1)
s += piece in a loop quadratic string building "".join(parts)
requests.get(url) with no timeout hangs forever (Ch 43.1, 64.3) timeout=(3, 10), a Session
verify=False, ssl.CERT_NONE anyone can intercept fix the CA bundle (Ch 45.1)
shell=True with input command injection (Ch 34.1) an argument list
pickle.load / yaml.load / torch.load(weights_only=False) on untrusted files remote code execution (Ch 47) JSON, safe_load, safetensors
f-strings in SQL SQL injection (Ch 49.1) placeholders
random for tokens predictable secrets (Ch 48.1)
== to compare secrets timing leak hmac.compare_digest
naive datetime.now() across systems wrong by hours aware UTC datetimes (Ch 19.1)
a Python loop over numbers in a hot path 100× slower NumPy/pandas/PyTorch vectorization (Ch 53)
time.sleep / requests inside async def blocks every request (Ch 29.5) await asyncio.sleep, httpx.AsyncClient, to_thread
bare asyncio.create_task and forget lost exceptions, leaked tasks TaskGroup (Ch 29.2)
threads for CPU-bound pure Python (GIL build) no speedup processes, or vectorize (Ch 27–28)
a huge utils.py everything depends on everything modules by responsibility (Ch 9)
deep inheritance for code reuse rigid, hard to follow composition, protocols (Ch 17)
classes with only __init__ and one method Java in Python a function, or a dataclass
unpinned dependencies in an app different program each install (Ch 21.4) uv.lock + --locked
notebooks as production code hidden state, no tests modules + tests; notebooks import them (Ch 62.3)
print debugging left in production noise, leaked data logging with levels (Ch 68)
model or data loaded at import time slow imports, surprise network calls load in main() or a lifespan hook (Ch 44)
fitting preprocessing on all data before the split leakage, fake accuracy (Ch 56.3) a Pipeline
one random seed, one run lucky results several seeds, report the spread (Ch 62.1)

ruff check --select ALL flags a large share of this table automatically; start from it and turn off the rules you disagree with, deliberately.


Generated from real-life-python-guide.md · 8 Oct 2026 16:36