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
| Gotcha | Rule | Full page |
|---|---|---|
| Mutable default argument | Use 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 closures | Closures capture the variable, not its value at creation — bind early with a default arg (lambda i=i: ...) | [[Python Scoping & Closures]] |
[[]] * n shallow reference | Creates n references to the same inner list — use a list comprehension instead | [[Identity, Mutability & Memory]] |
dataclass mutable default | Use field(default_factory=list), never tags: list = [] directly | [[Python OOP & Data Model]] |
@wraps omitted in a decorator | Wrapper silently loses __name__/__doc__ — breaks introspection and can collide route names in web frameworks | [[Python Decorators]] |
| Integer division | 7 / 2 == 3.5 (float, true division); 7 // 2 == 3 (floor division) | — |
+= on tuple vs list | On 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 comparison | 1 < x < 10 is valid Python, evaluated as 1 < x and x < 10 — not a syntax error, a real feature | — |
list.pop(0) for a queue | O(n) — every remaining element shifts. Use collections.deque and popleft() (O(1)) instead | — |
| GIL + threads for CPU work | Threads don't speed up CPU-bound code — the GIL serializes bytecode execution regardless of thread count | [[Python Concurrency]] |
Time Complexity — Common Operations
| Operation | list | dict / set |
|---|---|---|
x in collection | O(n) | O(1) average |
append | O(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 / delete | — | O(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]]