Key Takeaways
- Python interviews often test whether you understand why the language behaves a certain way, not just whether code runs correctly.
- Mutable default arguments are one of the most common Python gotchas interviewers ask about directly.
- List comprehensions and generator expressions are frequently confused — knowing when to use each signals real fluency.
- The GIL (Global Interpreter Lock) is a near-guaranteed topic in any interview that touches concurrency.
- Decorators and context managers come up often because they're widely used in production code but poorly understood by candidates who've only learned Python for scripting.
Python's readability makes it easy to write code that runs correctly without fully understanding why
— which is exactly the gap Python interviews are designed to probe. A candidate who's used Python
primarily for scripting or data work may not have encountered mutable default argument bugs, the
GIL's effect on threading, or the difference between @staticmethod and @classmethod in practice.
This guide covers the areas that come up most often, with the reasoning interviewers actually want
to hear.
What Python Interviews Actually Test
Python's flexibility is a double-edged sword in interviews: because so much "just works" without strict typing or explicit memory management, it's easy to have gaps that don't surface until someone asks a targeted question. Interviewers use a handful of classic gotchas specifically because they reveal whether a candidate has actually run into these issues in real code, versus only writing straightforward scripts.
Core Language Questions
"What's the difference between a list and a tuple, beyond mutability?" Lists are mutable and typically used for collections that change; tuples are immutable and slightly more memory-efficient, and their immutability makes them hashable — which means a tuple can be used as a dictionary key or set member, while a list cannot.
"What's wrong with this function?"
def add_item(item, items=[]):
items.append(item)
return items
This is one of the most common Python interview questions precisely because it looks correct. The
default argument [] is evaluated once, at function definition time, not on every call — so every
call that doesn't pass its own items list shares and mutates the same list object across calls.
The fix is to default to None and create a new list inside the function body:
def add_item(item, items=None):
if items is None:
items = []
items.append(item)
return items
"Explain *args and
**kwargs." \*argscollects extra positional arguments into a tuple;\*\*kwargs collects extra
keyword arguments into a dictionary. They're commonly used together in wrapper functions and
decorators, where you need to forward an arbitrary set of arguments to another function without
knowing its exact signature in advance.
List Comprehensions vs. Generator Expressions
squares_list = [x * x for x in range(1_000_000)] # builds the full list in memory
squares_gen = (x * x for x in range(1_000_000)) # lazily yields one value at a time
A strong answer explains the trade-off directly: a list comprehension builds the entire result in memory immediately, while a generator expression produces values lazily, one at a time, which matters when working with large or unbounded sequences where holding the full result in memory isn't necessary or feasible.
If the interviewer asks about generator expressions, it's worth proactively mentioning generator
functions (using yield) as the related but distinct pattern for writing more complex lazy
iteration logic — it shows you're thinking beyond the syntax to the underlying pattern.
OOP and Design Questions
"What's the difference between @staticmethod and @classmethod?"
class User:
count = 0
def __init__(self, name):
self.name = name
User.count += 1
@classmethod
def from_dict(cls, data):
return cls(data["name"]) # cls refers to the class itself, supports subclassing correctly
@staticmethod
def is_valid_name(name):
return isinstance(name, str) and len(name) > 0 # no access to class or instance at all
@classmethod receives the class itself (cls) as its first argument, which makes it useful for
alternate constructors that need to work correctly with subclasses. @staticmethod receives neither
the instance nor the class — it's essentially a regular function namespaced inside the class for
organizational purposes.
"What is duck typing, and how does Python use it?" Duck typing means an object's suitability is determined by whether it implements the expected methods or behavior, not by its explicit type or class hierarchy — "if it walks like a duck and quacks like a duck." Python leans heavily on this: a function that iterates over its argument doesn't require that argument to be a specific type, only that it supports iteration.
Python's flexibility means most interview questions aren't really testing syntax — they're testing whether you've been burned by the language's quirks in real production code.
Common Coding Exercise Pattern
A frequent live-coding request: write a function that groups items by a key.
from collections import defaultdict
def group_by(items, key_fn):
groups = defaultdict(list)
for item in items:
groups[key_fn(item)].append(item)
return dict(groups)
group_by(["apple", "banana", "avocado"], key_fn=lambda w: w[0])
# {'a': ['apple', 'avocado'], 'b': ['banana']}
Interviewers are watching for whether you reach for defaultdict instead of manually checking
if key not in groups on every iteration, and whether you can explain your choice of data structure
without being asked.
Concurrency Questions
"What is the GIL, and how does it affect multithreading?" The Global Interpreter Lock ensures only one thread executes Python bytecode at a time within a single process, even on multi-core machines. This means CPU-bound multithreaded Python code often sees little to no speedup from adding threads, since threads still take turns holding the GIL. I/O-bound code (waiting on network calls or disk) benefits from threading anyway, because the GIL is released during blocking I/O operations.
"When would you use multiprocessing instead of threading?" For CPU-bound work, since each process gets its own Python interpreter and its own GIL, allowing genuine parallel execution across cores — at the cost of higher memory overhead and more complex inter-process communication compared to threads sharing memory directly.
"What about asyncio?" asyncio provides cooperative concurrency within a single thread — well
suited to I/O-bound workloads with many concurrent operations (network requests, database calls)
where the overhead of threads or processes isn't justified, but it requires the entire call chain to
be written with async/await to actually benefit.
Put this into practice.
Start a free AI-powered mock interview — real follow-up questions, instant feedback, no card required.
Common Mistakes
- Using a mutable default argument without realizing it's shared across calls
- Confusing
is(identity comparison) with==(value equality), especially with small integers or strings where Python's internal caching can makeismisleadingly appear to work - Not knowing that dictionaries in modern Python (3.7+) maintain insertion order, which occasionally surprises candidates who learned an older version of the language
- Reaching for threading to speed up CPU-bound work without accounting for the GIL
How to Prepare
Beyond reviewing syntax, revisit a few real Python bugs you've personally hit — a mutable default argument, an off-by-one in a slice, a threading issue that turned out to be GIL-related. These make for much stronger interview answers than textbook definitions, because they demonstrate you've actually debugged the language's quirks rather than just read about them.
Put this into practice.
Start a free AI-powered mock interview — real follow-up questions, instant feedback, no card required.