Python105 min total · 18 parts
Python Fundamentals for Interviews: Data Structures, Comprehensions, and Gotchas
Part 18 of 18 · ~3 min
Common Mistakes Worth Remembering
Every one of these is a bug this watcher actually had, at some point in this reference:
offenders=[]sitting in a function signature, orflagged = []sitting on a class — one shared object slowly absorbing state from every unrelated caller instead of starting fresh each time, because it was only ever built once.- Checking
status is 401instead ofstatus == 401— passes every test built around small, commonly-cached numbers, then fails without a single error the day the value in question falls outside that range. - Pulling two different answers out of one generator with two separate
sum()calls, and not noticing the second answer is zero rather than merely wrong. - Writing
[lambda: i for i in range(3)]-shaped code and expecting three different captured values — every one of those closures shares the samei, so all three see whateverifinished at, not whatever it was when each closure was written. - Writing a bare
except:to "handle everything," and discovering it also intercepts the Ctrl+C you were using to stop the program. - Adding
__eq__to a class for one unrelated reason and later being unable to explain why a completely different part of the codebase can no longer put instances of it in aset. - Decorating an
async defwith a decorator that was written for ordinary functions — it looks like it works, because calling a coroutine function never itself raises; the decorator's own logic never runs at all, and the real failure only surfaces once something finallyawaits the result. - Scanning a
listwithininside a loop where asetwould turn that same check from O(n) into O(1). - Growing a string with
+=inside a loop instead of collecting the pieces and callingstr.joinonce, turning what should be O(n) work into O(n²).
The same bugs, somewhere else entirely
One risk of building a whole reference around a single running program: it's possible to learn "the log watcher's bugs" instead of "the actual bugs." So here are two of them again, with no IPs, no logs, and no rate limits anywhere in sight — a quiz-grading tool:
def grade_quiz(answers, wrong=[]): # same shape as flag_offenders, new costume
for question, given in answers.items():
if given != ANSWER_KEY[question]:
wrong.append(question)
return wrong
Grade one student, and the next student's "wrong answers" list starts out already containing the first student's mistakes — identical mechanism, nothing to do with servers or requests.
pass_thresholds = []
for min_score in [50, 75, 90]: # pass / merit / distinction
pass_thresholds.append(lambda score: score >= min_score)
[check(80) for check in pass_thresholds] # [True, True, True] — every check uses 90, the last value
If both of those read as immediately familiar rather than as new bugs to figure out, the underlying mechanism transferred — which was the entire point of building one program instead of nine unrelated ones.
The code lab is the place to actually type these out rather than just read them — start with the mutable default and the loop-closure trap, since those two are the ones most likely to show up disguised as someone else's bug in an interview. And once a request has made it past this watcher, Django Fundamentals picks up the other half of the story: what actually handles it on the way in.
Practice this
Continue learning
- PythonDjango Fundamentals: The ORM, Migrations, and Shipping a Real App
- Interview & Career PrepThe Non-Technical Half of the Interview: Behavioral Questions, the STAR Method, and What Recruiters Are Actually Scoring
- AI & LLM EngineeringAI & LLM Engineering Fundamentals: Prompting, RAG, Embeddings, and Function Calling