Python-Specific Rapid Fire
Topic 7 of 7, with 3 concept checks. Cross-tier gotchas that come up constantly in Python interviews
Make Python semantics work in your favor
Python fluency
Recall the language behaviors interviewers probe most often, explain their consequences precisely, and prefer readable built-ins and protocols over clever shorthand.
Core lesson 01
Say: 'Default argument values are evaluated once, when the function is defined -- not once per call -- so a mutable default is one shared object across every call that doesn't pass its own.'
Interviewers are listening for whether you understand WHY, not just THAT it's a bug. The precise, complete explanation names the mechanism (evaluated once, at def time) rather than just the symptom (weird accumulating list) -- that's the difference between reciting a memorized gotcha and actually understanding Python's object model.
def append_to(item, target=[]):
target.append(item)
return target
print(append_to(1)) # [1]
print(append_to(2)) # [1, 2] -- same default list, reusedWhat to remember
In a whiteboard interview, what's the fastest way to explain why def f(x=[]): is dangerous, in one sentence?
Common footguns
- Explaining this as just 'lists are mutable' without mentioning the ONE-TIME creation at def time -- technically incomplete, and most interviewers will probe further.
Core lesson 02
Say: 'CPython's GIL means only one thread executes Python bytecode at a time, so threading speeds up I/O-bound work but not CPU-bound work -- for that, use multiprocessing instead.'
This is one of the most commonly asked 'do you actually understand Python internals' questions. A strong answer states the practical decision rule (threading for I/O, multiprocessing for CPU) FIRST, then backs it up with the mechanism (GIL), rather than launching straight into interpreter internals without connecting it to when it actually matters.
# threading: great here (I/O-bound, GIL released during wait)
# multiple threads fetching URLs concurrently
# multiprocessing: needed here (CPU-bound, GIL never released for pure compute)
# multiple processes each crunching a chunk of a large computationWhat to remember
What's the crispest way to explain the GIL's practical impact in an interview?
Common footguns
- Reciting GIL trivia without connecting it to the practical threading-vs-multiprocessing decision -- interviewers usually care more about the judgment call than the trivia.
Core lesson 03
No copy (b = a) means literally the same object. Shallow copy (a.copy() or a[:]) creates a new outer container but nested objects are still shared. Deep copy (copy.deepcopy(a)) recursively copies everything.
This distinction comes up constantly, both as a direct question and as a hidden bug in larger design questions (e.g. 'why did modifying one row of my grid modify every row?' -- usually a shallow-copy-of-nested-lists bug from something like [[0]*n]*n). Being able to state the three-way distinction crisply, with the mutation test, signals real understanding.
import copy
original = [[1, 2], [3, 4]]
no_copy = original
shallow = original.copy()
deep = copy.deepcopy(original)
shallow[0].append(99)
print(original) # [[1, 2, 99], [3, 4]] -- shallow copy leaked the mutation
deep[0].append(-1)
print(original) # unaffected -- deep copy is fully independentWhat to remember
How do you quickly distinguish shallow copy, deep copy, and no copy at all in an interview?
Common footguns
- The classic grid-initialization bug: rows = [[0] * n] * m creates m references to the SAME inner list -- mutating one row mutates all of them. Use [[0] * n for _ in range(m)] instead.
Python lab
Browser Python lab
Runtime · idle
Python loads on your first run. Your code stays in this browser.
Best practices
- When explaining a gotcha in an interview, name the underlying mechanism, not just the symptom.
- Practice the 'does mutating a nested value leak through?' test for instantly classifying copy behavior.
- Connect GIL/concurrency trivia to the practical threading-vs-multiprocessing-vs-asyncio decision, not just the definition.
- Revisit the Beginner and Intermediate tiers' footgun lists the night before an interview -- they're the highest-frequency trick questions.
Apply the concept in Interview practice
Two SumeasyLeetCode #1 · O(n) time
The single most-asked interview question across all companies -- make sure the hash-map approach is completely automatic.
Open problemValid ParentheseseasyLeetCode #20 · O(n) time
Push opening brackets onto a stack; on a closing bracket, check it matches the top of the stack before popping -- classic stack warm-up question.
Open problemMerge IntervalsmediumLeetCode #56 · O(n log n) time
Sort intervals by start time, then merge any interval whose start is <= the current merged interval's end.
Open problemBest Time to Buy and Sell StockeasyLeetCode #121 · O(n) time
Track the minimum price seen so far while scanning once; at each day, check if selling today beats the best profit found so far.
Open problemConcept checks
In a whiteboard interview, what's the fastest way to explain why def f(x=[]): is dangerous, in one sentence?
Hint
Focus on WHEN the default is created, not just 'lists are mutable'.
The killer detail: it's created ONCE, at definition time, and shared thereafter.
Answer
Say: 'Default argument values are evaluated once, when the function is defined -- not once per call -- so a mutable default is one shared object across every call that doesn't pass its own.'
Interviewers are listening for whether you understand WHY, not just THAT it's a bug. The precise, complete explanation names the mechanism (evaluated once, at def time) rather than just the symptom (weird accumulating list) -- that's the difference between reciting a memorized gotcha and actually understanding Python's object model.
def append_to(item, target=[]):
target.append(item)
return target
print(append_to(1)) # [1]
print(append_to(2)) # [1, 2] -- same default list, reusedWatch out
- Explaining this as just 'lists are mutable' without mentioning the ONE-TIME creation at def time -- technically incomplete, and most interviewers will probe further.
What's the crispest way to explain the GIL's practical impact in an interview?
Hint
Lead with the practical consequence, then the mechanism, not the other way around.
Practical consequence: threading helps I/O-bound code, not CPU-bound code.
Answer
Say: 'CPython's GIL means only one thread executes Python bytecode at a time, so threading speeds up I/O-bound work but not CPU-bound work -- for that, use multiprocessing instead.'
This is one of the most commonly asked 'do you actually understand Python internals' questions. A strong answer states the practical decision rule (threading for I/O, multiprocessing for CPU) FIRST, then backs it up with the mechanism (GIL), rather than launching straight into interpreter internals without connecting it to when it actually matters.
# threading: great here (I/O-bound, GIL released during wait)
# multiple threads fetching URLs concurrently
# multiprocessing: needed here (CPU-bound, GIL never released for pure compute)
# multiple processes each crunching a chunk of a large computationWatch out
- Reciting GIL trivia without connecting it to the practical threading-vs-multiprocessing decision -- interviewers usually care more about the judgment call than the trivia.
How do you quickly distinguish shallow copy, deep copy, and no copy at all in an interview?
Hint
Ask yourself: after copying, if I mutate a NESTED object, does the original change?
No copy: same object, obviously affects original. Shallow: nested objects still shared. Deep: fully independent.
Answer
No copy (b = a) means literally the same object. Shallow copy (a.copy() or a[:]) creates a new outer container but nested objects are still shared. Deep copy (copy.deepcopy(a)) recursively copies everything.
This distinction comes up constantly, both as a direct question and as a hidden bug in larger design questions (e.g. 'why did modifying one row of my grid modify every row?' -- usually a shallow-copy-of-nested-lists bug from something like [[0]*n]*n). Being able to state the three-way distinction crisply, with the mutation test, signals real understanding.
import copy
original = [[1, 2], [3, 4]]
no_copy = original
shallow = original.copy()
deep = copy.deepcopy(original)
shallow[0].append(99)
print(original) # [[1, 2, 99], [3, 4]] -- shallow copy leaked the mutation
deep[0].append(-1)
print(original) # unaffected -- deep copy is fully independentWatch out
- The classic grid-initialization bug: rows = [[0] * n] * m creates m references to the SAME inner list -- mutating one row mutates all of them. Use [[0] * n for _ in range(m)] instead.