Back to Notes

Python Gotchas Cheat Sheet

Fast last-minute-review reference. Full explanations for the non-obvious ones live on their dedicated pages — this is the recall table, not the teaching material.

Quick-Recall Table

GotchaRuleFull page
Mutable default argumentUse None as default, initialize inside the function body[[Identity, Mutability & Memory]]
is vs ==Use == for value comparison; is only for None/singleton checks[[Identity, Mutability & Memory]]
Late binding in closuresClosures capture the variable, not its value at creation — bind early with a default arg (lambda i=i: ...)[[Python Scoping & Closures]]
[[]] * n shallow referenceCreates n references to the same inner list — use a list comprehension instead[[Identity, Mutability & Memory]]
dataclass mutable defaultUse field(default_factory=list), never tags: list = [] directly[[Python OOP & Data Model]]
@wraps omitted in a decoratorWrapper silently loses __name__/__doc__ — breaks introspection and can collide route names in web frameworks[[Python Decorators]]
Integer division7 / 2 == 3.5 (float, true division); 7 // 2 == 3 (floor division)
+= on tuple vs listOn a tuple, creates a new tuple (immutable); on a list, mutates in place[[Identity, Mutability & Memory]]
Bare except:Catches SystemExit/KeyboardInterrupt too — always use except Exception at minimum, never a bare except:
Chained comparison1 < x < 10 is valid Python, evaluated as 1 < x and x < 10 — not a syntax error, a real feature
list.pop(0) for a queueO(n) — every remaining element shifts. Use collections.deque and popleft() (O(1)) instead
GIL + threads for CPU workThreads don't speed up CPU-bound code — the GIL serializes bytecode execution regardless of thread count[[Python Concurrency]]

Time Complexity — Common Operations

Operationlistdict / set
x in collectionO(n)O(1) average
appendO(1) amortized
insert(0, x)O(n)
pop() (from end)O(1)
pop(0) (from front)O(n) — never do this for a queue
get / set / deleteO(1) average
sorted()O(n log n)

Useful Built-ins for DSA / Interview Coding

sorted(lst, key=lambda x: x[1])       # sort by second element of each tuple
heapq.nlargest(k, lst)                # top-k without a full sort
collections.Counter(lst)              # frequency map, .most_common(k)
collections.defaultdict(list)         # graph adjacency list, no KeyError on missing keys
collections.deque(maxlen=k)           # sliding window / O(1) both-ends queue
bisect.bisect_left(lst, target)       # binary search insertion point on a sorted list

float('inf'), float('-inf')           # infinities for comparison sentinels
divmod(7, 2)                          # (3, 1) — quotient and remainder in one call

''.join(lst)                          # join a list of characters into a string
s[::-1]                               # reverse a string
ord('a'), chr(97)                     # char <-> int code point, inverses of each other

a, b = b, a                           # swap without a temp variable

Interview Drill

Q: You're asked to implement a BFS queue in Python. What's the one-line mistake that silently makes it O(n²) instead of O(n)? A: Using a plain list with list.pop(0) to dequeue — each pop shifts every remaining element down one index, making a full BFS O(n²) instead of O(n). Use collections.deque and popleft(), which is O(1), specifically because it's implemented as a doubly-linked structure rather than a contiguous array.

Cross-links

[[Identity, Mutability & Memory]] · [[Python Scoping & Closures]] · [[Python Decorators]] · [[Python OOP & Data Model]] · [[Python Concurrency]]