Python interview questions: 25 questions with the reasoning behind them
Not a list of definitions to memorise, but what each question is really testing — and how to answer so that it shows.
There is no shortage of "100 Python questions" lists, and they help less than they promise: a memorised definition of a decorator is audible from the first sentence. The interviewer almost always asks the next question — "so why do you need functools.wraps?" — and that is where the script runs out.
Data model
Which types are mutable?
Immutable: int, float, str, tuple, frozenset, bytes. Mutable: list, dict, set, bytearray.
A good answer does not stop at the list — it goes straight to the consequence: a mutable object passed into a function is changed for the caller too.
What is wrong with this code?
def add_item(item, basket=[]):
basket.append(item)
return basket
The list is created once, when the function is defined, not on each call. So the second call returns both items, which is never what anyone wanted.
def add_item(item, basket=None):
if basket is None:
basket = []
basket.append(item)
return basket
How does is differ from ==?
== compares values, is compares identity. The follow-up is usually about small integers: 256 is 256 is True, 257 is 257 is not, because CPython caches integers from −5 to 256. The right conclusion is not "memorise this" but "which is why is is only used against None, True and False".
Functions and decorators
What is a decorator?
import functools, time
def timed(fn):
@functools.wraps(fn)
def wrapper(*args, **kwargs):
start = time.perf_counter()
try:
return fn(*args, **kwargs)
finally:
print(f"{fn.__name__}: {time.perf_counter() - start:.3f}s")
return wrapper
Two details carry the answer. functools.wraps copies the name and docstring onto the wrapper — without it fn.__name__ becomes "wrapper", breaking anything that relies on introspection. And try/finally means the timing still prints when the call raises.
What is a closure?
def counter():
n = 0
def inc():
nonlocal n # without nonlocal: UnboundLocalError
n += 1
return n
return inc
nonlocal is the reason the question gets asked.
Generators
How does a generator differ from a list?
It produces items one at a time and never holds them all. A list comprehension over ten million items costs hundreds of megabytes; the generator expression costs one item's worth.
sum(x * x for x in range(10_000_000)) # constant memory
sum([x * x for x in range(10_000_000)]) # roughly 400 MB
The price: you cannot iterate twice and you cannot call len().
What is the iterator protocol?
An object is iterable if it has __iter__ returning an iterator. An iterator has __next__, which yields the next item and raises StopIteration when exhausted. A for loop is sugar over exactly that.
The GIL
What is the GIL and what does it prevent?
The Global Interpreter Lock means only one thread executes CPython bytecode at a time, so threads do not speed up computation across cores.
The follow-up is always: then why have threads at all? Because the GIL is released during I/O. While one thread waits on the network or disk, others run — so threads help with API calls and not with matrix multiplication.
For computation: multiprocessing, or libraries that release the GIL in C code such as NumPy. For I/O: threads or asyncio. Python 3.13 ships an experimental free-threaded build, worth mentioning but not worth building an answer on.
How does asyncio differ from threads?
Threads are switched by the operating system at arbitrary points; coroutines are switched by the event loop, and only at an await. So between two awaits you know you were not interrupted, which removes a whole class of races. The cost: one blocking call stalls the entire loop.
Classes
What is the MRO?
Method Resolution Order — the order Python searches for a method under multiple inheritance, computed by the C3 algorithm and visible as __mro__. It is also the reason to call super().__init__() rather than Base.__init__(self): the latter breaks the chain in diamond inheritance.
staticmethod or classmethod?
@classmethod receives the class, so it behaves correctly under inheritance — the usual use is an alternative constructor. @staticmethod receives nothing and simply lives in the class namespace.
class User:
@classmethod
def from_json(cls, raw):
return cls(**json.loads(raw)) # a subclass returns a subclass
How does a context manager work?
An object with __enter__ and __exit__. The second always runs, including on an exception, and suppresses it if it returns a truthy value.
from contextlib import contextmanager
@contextmanager
def transaction(conn):
try:
yield conn
conn.commit()
except Exception:
conn.rollback()
raise
Practical questions
How would you find a bottleneck?
This is about method, not tooling. Measure first (cProfile, line_profiler), then look at algorithmic complexity, and only then at micro-optimisation. "I would rewrite it in C" without measurement is a bad sign.
Why is a dict better than a list for lookups?
Average constant time against a linear scan. On a thousand items nobody notices; on a million it decides everything. Worth adding that hash collisions degrade it to linear in the worst case, rare as that is in practice.
When you do not know the answer
It happens to everyone, and the reaction matters more than the knowledge. The worst option is inventing something: the interviewer knows this particular topic better than you, and it shows.
"I have not used __slots__ in production. From the name, and from the fact that instances normally carry a __dict__, I would guess it fixes the attribute set and saves memory. Is that right?"
That is a strong answer — it shows you can reason from first principles rather than only recall.
In short
- Every basic question has a follow-up: prepare the consequence, not the definition.
- Mutable defaults,
functools.wraps,nonlocaland the MRO are where experience shows. - The GIL blocks computation, not I/O.
- Answer performance questions with "measure first".
- Unknown territory: reason aloud from what you do know.
Shpora is an AI assistant that hears the interviewer's question and gives you something to build an answer on within a second. It runs on your own computer, with any video call.
Try it free