Python interview questions have a reputation for being easy, and the first ten minutes usually are. Then someone shows you four lines of code with a default argument and a list, asks what gets printed, and the interview is suddenly about whether you know what the interpreter does rather than what the code looks like it does.
That is the pattern behind nearly every question in a Python loop in 2026. The syntax is simple on purpose; the model underneath is where the questions live. Five areas cover almost all of it:
- The data model - names, references, mutability and copies
- Functions and idioms - generators, decorators, closures and context managers
- Classes and protocols - duck typing, special methods, inheritance and metaclasses
- Concurrency and memory - the GIL, threads, processes, asyncio and garbage collection
- Errors, packaging and tooling - exceptions, environments, dependencies and types
Try the quiz before reading on. Five questions is enough to show where your model has holes.
Start here: a 5-question self-check
Five questions from the Python set on squizzu, one each on the data model, classes, concurrency, memory and errors. You get an explanation as soon as you answer and a deeper one below it. No account needed.
The GIL question splits candidates more reliably than any other, because "Python cannot do multithreading" and "the GIL does not matter" are both wrong, and interviewers ask it to hear which of the two you believe. The other common gap is copying: plenty of experienced developers use slicing to copy a list without realising the nested objects are still shared. If either cost you a question, read areas 1 and 4 first.
What a Python interview looks like in 2026
Python shows up in backend, data, machine learning and automation loops, and the format adapts to each, but three kinds of question recur.
Output prediction. A short snippet, and the question is what it prints. These are quick filters for the mental model: mutable defaults, closures in loops, shallow copies, is versus ==, exceptions inside finally.
Idiomatic coding. A practical task, such as parsing a log file or aggregating records, where the interviewer watches whether you reach for comprehensions, generators, collections and context managers, or write Java in Python syntax.
Design and trade-offs. How would you make this service handle more requests? Why asyncio here and processes there? How would you package and test it? These questions matter more for senior roles.
The 2026 additions are practical rather than exotic. Type hints and a type checker are expected in any serious codebase. Packaging has settled on pyproject.toml, with fast tools such as uv becoming the default in many teams. And Python 3.14 made the free-threaded build officially supported, which means the GIL question now has a second half: what changes when the lock is gone.
Area 1: The data model: names, mutability and copies
Everything in this area follows from one sentence: a Python variable is a name bound to an object, not a box that contains a value. Assignment never copies. It binds another name to the same object.
That is harmless for immutable objects such as integers, strings and tuples, because nothing can change them in place. It matters a great deal for mutable ones:
a = [1, 2, 3]
b = a # same list, two names
b.append(4)
print(a) # [1, 2, 3, 4]
Copies come in two depths. A shallow copy, which is what slicing, list(x), dict(x) and the .copy() method all produce, creates a new outer container whose elements are references to the same inner objects. For a flat list of numbers that is indistinguishable from a full copy. For a list of lists it is not: changing an inner list through the copy changes it in the original too. copy.deepcopy recursively copies the nested objects as well, at the cost of time and memory.
Mutable default arguments are the most famous consequence. Default values are evaluated once, when the def statement runs, so a default of [] is one list shared by every call that does not pass the argument:
def add(item, bucket=[]):
bucket.append(item)
return bucket
add(1) # [1]
add(2) # [1, 2] - the same list as before
The fix is to default to None and create the list inside the function.
You will also get a couple of quick checks. is compares identity, == compares value: is asks whether two names point at the same object, and should be reserved for singletons such as None. And only hashable objects can be dictionary keys or set members, which in practice means immutable ones: a tuple of strings works, a list does not.
What the interviewer is testing: whether you can predict what code does from the object model instead of from how it looks. Common follow-up: "Why can a tuple containing a list not be used as a dictionary key?" Because the tuple is only as immutable as what it contains, so it cannot be hashed.
Area 2: Functions, generators and decorators
Functions in Python are objects, which is what makes most of the idioms in this area possible.
Generators produce values lazily. A function containing yield returns a generator object, and each call to next() runs the function until the next yield. That means a pipeline of generators can process a file larger than memory one line at a time. Two properties get asked about: a generator can be iterated only once, after which it is exhausted, and a generator expression ((x * 2 for x in data)) is the lazy counterpart of a list comprehension.
Closures capture variables, not values. The classic trap is creating functions in a loop:
funcs = [lambda: i for i in range(3)]
print([f() for f in funcs]) # [2, 2, 2]
Each lambda looks up i when it is called, by which time the loop has finished. Binding the current value as a default argument (lambda i=i: i) fixes it.
Decorators are functions that take a function and return a replacement, and @decorator above a def is shorthand for f = decorator(f). Caching, retries, timing, access checks and route registration in web frameworks are all decorators. The detail that makes the answer complete is functools.wraps, which copies the original function's name and docstring onto the wrapper so that debugging and introspection still work.
Context managers guarantee cleanup. A with block calls __enter__ on the way in and __exit__ on the way out, whether the block finishes normally or raises. That is why files, locks and database transactions are opened with with, and why contextlib.contextmanager turns a generator into a context manager in a few lines.
What the interviewer is testing: idiomatic Python, not only correct Python. Common follow-up: "How would you write a decorator that takes arguments?" One more level of nesting: a function that takes the arguments and returns the actual decorator.
Area 3: Classes, protocols and duck typing
Python's approach to types is duck typing: code cares about what an object can do, not what class it belongs to. A function that iterates over its argument works with a list, a generator, a file or any object with an __iter__ method, and nobody had to declare an interface. Type hints formalise the same idea with typing.Protocol, which describes the methods an object must have without requiring it to inherit from anything.
Special methods are how objects plug into the language. __len__ makes len() work, __eq__ and __hash__ control equality and dictionary keys, __repr__ controls how an object appears in logs and the REPL, __iter__ makes it iterable, and __enter__ and __exit__ make it a context manager. The pairing rule worth knowing: if you define __eq__, Python sets __hash__ to None unless you define it too, because equal objects must hash equally.
Inheritance in Python supports multiple base classes, which is why super() does not simply mean "the parent". It follows the method resolution order, a linearised list of classes that you can inspect with ClassName.__mro__, and calling super() consistently is what lets cooperative multiple inheritance work.
Dataclasses remove most of the boilerplate for classes that mainly hold data: @dataclass generates __init__, __repr__ and __eq__ from annotated fields, and frozen=True makes instances immutable and hashable. They are usually the right answer to "how would you model this record?".
Metaclasses are the advanced end of this area. Classes are objects too, and a metaclass is the class of a class: it controls how a class object is created. Frameworks use them, for example to register every model class automatically, but most application code never needs one, and __init_subclass__ or a class decorator covers many of the same cases more simply. Knowing what a metaclass is and when not to use one is the answer interviewers want.
What the interviewer is testing: designing around behaviour instead of rigid class hierarchies. Common follow-up: "When would you use __slots__?" For classes with very many instances: it replaces the per-instance __dict__ with fixed storage, saving memory and preventing new attributes from being added.
Area 4: Concurrency and memory
This is the area almost every backend and data interview visits, and it starts with the Global Interpreter Lock. In the standard CPython build, only one thread executes Python bytecode at a time. The lock is released while a thread waits on I/O, and many C extensions release it during heavy computation, so the practical rule is:
- I/O-bound work (network calls, disk, databases) - threads or asyncio both help, because most of the time is spent waiting.
- CPU-bound pure Python - threads do not help on the standard build. Use
multiprocessingorconcurrent.futures.ProcessPoolExecutor, or move the hot loop into a library such as NumPy that does its work outside the interpreter.
asyncio is a third model: a single thread runs an event loop, and coroutines give up control at each await, so thousands of network connections can be handled without a thread each. Its rule is that nothing in a coroutine may block. One synchronous call, such as time.sleep or a blocking HTTP client, stalls every other task on the loop.
Free-threading changes the second bullet. Python 3.14 made the free-threaded build, often installed as python3.14t, officially supported. Without the GIL, threads can run Python code on several cores at once, which makes shared mutable state a real concern again: code that was accidentally safe because of the lock now needs proper locks. It is still a separate, opt-in build, and extensions must declare support for it, so the honest 2026 answer is "promising, and worth testing, but not the default".
Memory is managed automatically, mostly by reference counting: every object tracks how many references point at it and is freed the moment the count reaches zero. Reference counting cannot free objects that refer to each other in a cycle, so a separate cyclic garbage collector runs periodically to find and collect unreachable cycles. Memory leaks are still possible in Python, but they come from references you forgot about, such as ever-growing caches or global lists, rather than from missing free calls.
What the interviewer is testing: whether you can pick a concurrency model from the workload. Common follow-up: "Your async service slowed down after someone added a library call. Why?" Almost certainly a blocking call on the event loop, fixed by using an async client or asyncio.to_thread.
Area 5: Errors, packaging and tooling
Exceptions propagate up the call stack until an except clause handles them. If nothing does, the program terminates and prints a traceback. The structure of a try statement is a standard check: except handles the error, else runs only if nothing was raised, and finally runs no matter what, even when the try block returns.
The best-practice questions are about restraint. Catch the specific exceptions you expect and can handle. Avoid a bare except:, which also catches KeyboardInterrupt and SystemExit and hides bugs, and prefer except Exception if you truly need a catch-all. Use raise ... from err when translating one exception into another, so the original cause survives in the traceback.
Environments and dependencies are where production Python goes wrong most often. A virtual environment gives each project its own interpreter and packages, so two projects can depend on different versions of the same library without conflict. Dependencies are declared in pyproject.toml, and a lock file records the exact resolved versions, including indirect ones, so every machine installs the same set. uv, Poetry and pip-tools all produce one. Installing packages into the system interpreter is the answer interviewers do not want to hear.
Type hints and tests round out the modern answer. Hints are not enforced at runtime, but a checker such as mypy or pyright catches a whole class of bugs before they run, and libraries such as Pydantic use the same annotations to validate data at runtime. pytest, with fixtures and parametrised tests, is the default test runner.
What the interviewer is testing: whether your code is safe to run in production and reproducible on someone else's machine. Common follow-up: "What is the difference between requirements.txt and pyproject.toml?" The first is a list of packages to install into an environment; the second describes the project itself, including its dependencies and how to build it.
A one-week Python interview plan
For people who write Python most days. The goal is to stop relying on habit and start predicting what the interpreter will do.
- Day 1 - References. Write ten short snippets involving assignment, slicing, nested lists and default arguments. Predict each output before running it and note every surprise.
- Day 2 - Idioms. Rewrite a loop-heavy script with comprehensions, generators,
Counteranddefaultdict. Then write a decorator with arguments that usesfunctools.wraps. - Day 3 - Classes. Model a small domain with dataclasses, add
__repr__,__eq__and ordering, and make one class iterable. Inspect the MRO of a class with two base classes. - Day 4 - Concurrency. Time a CPU-bound function with a thread pool and a process pool. Then time an I/O-bound task with threads and with asyncio, and add one blocking call to the async version to see what happens.
- Day 5 - Free-threading. If you can, install the free-threaded 3.14 build and rerun the thread-pool test from day 4. Note which libraries in your stack support it.
- Day 6 - Errors and tooling. Create a fresh project with a virtual environment,
pyproject.tomland a lock file, add type hints to one module and run a type checker over it. - Day 7 - Rehearse. Explain out loud, in two minutes each: names and mutability, the GIL and when to use threads, processes or asyncio, and how an exception travels up the stack.
Keep practising
Five questions barely scratch the language. The full Python set is much bigger, with more than 200 reviewed questions across fundamentals, data structures, functions, classes, exceptions, memory, concurrency and packaging.
Work through the Python questions on squizzu. Output-prediction questions reward knowing why the wrong answers are wrong, so every explanation covers them too.
If your loop is for a backend role, the FastAPI quiz covers a framework many new Python services are built on, and the system design round is covered separately. For data roles, data engineer interview questions for 2026 takes Python into Spark and Kafka, and if you are heading towards AI work, how to prepare for an AI engineer interview covers the model side.
