Python interviews test fundamentals more than syntax. The 25 questions below cover the concepts interviewers actually ask: GIL, decorators, generators, async/await, dataclasses, and modern typing. Each answer is structured so you can adapt it to your own voice.
Core concepts (Q1–Q10)
- GIL — the Global Interpreter Lock allows only one thread to execute Python bytecode. CPU-bound work does not parallelise across threads.
- List vs tuple — lists are mutable, tuples are immutable. Tuples can be dict keys.
- Mutable default arguments — classic gotcha. Defaults are evaluated once, at function definition.
- *args / **kwargs — variable positional and keyword arguments.
- List comprehension vs generator expression — comprehensions build a list; generator expressions yield lazily.
- Decorators — functions that wrap other functions. Common for logging, auth, retry.
- Context managers — `with` blocks guarantee cleanup. Implement via `__enter__`/`__exit__` or `@contextmanager`.
- `is` vs `==` — `is` checks identity (same object), `==` checks equality.
- Deep vs shallow copy — `copy.copy()` is shallow (references), `copy.deepcopy()` is recursive.
- `__slots__` — class attribute that declares allowed fields; eliminates `__dict__`, reduces memory.
Intermediate (Q11–Q17)
- Dataclasses — `@dataclass` auto-generates `__init__`, `__repr__`, `__eq__`. Use `frozen=True` for immutability.
- Type hints — PEP 484 annotations. Static analysers catch bugs without runtime cost.
- Async vs threads — async is for I/O-bound concurrency; threads are blocked by the GIL for CPU work.
- `yield from` — delegates to a sub-iterator. Cleaner than `for x in sub: yield x`.
- MRO — Method Resolution Order, computed by C3 linearisation. `Class.__mro__` shows it.
- Descriptors — `__get__` / `__set__` / `__delete__`. `property`, `classmethod`, `staticmethod` are descriptors.
- `__init__.py` vs namespace packages — explicit packages still need `__init__.py`; PEP 420 added namespace packages.
Advanced (Q18–Q25)
- Structural pattern matching — `match`/`case` (3.10+). Supports literal, sequence, mapping, class patterns.
- Protocols — PEP 544 structural typing. `class Sized(Protocol)` is satisfied by anything with `__len__`.
- Coroutines vs generators — both use `yield`, but async coroutines also support `await` and `asyncio` scheduling.
- `asyncio.gather` vs `asyncio.wait` — gather returns coroutine results; wait returns (done, pending) sets.
- `weakref` — holds an object without preventing GC. Useful for caches.
- `__init_subclass__` — hook called whenever a subclass is created. The basis for class registries.
- `concurrent.futures` — high-level thread + process pool. `ProcessPoolExecutor` bypasses the GIL for CPU work.
- `typing.Final` + `@final` — mark names or methods as final; static checkers enforce.
How to structure your answer (the STAR-Python method)
Interviewers in 2026 expect Python candidates to structure answers as: definition (one sentence), trade-offs (one or two sentences), code example (3–10 lines), common pitfall. That four-part answer scores higher than long-winded explanations. Practise it on every question above.
- Definition — say the name of the concept and one sentence describing what it does.
- Trade-offs — when to use, when not to use, what it replaces.
- Code — write 3–10 lines that demonstrate the concept in isolation.
- Pitfall — the one thing that trips people up.
Performance, memory, and the GIL — practical impact
The GIL remains the single most misunderstood aspect of Python performance. It does not make Python single-threaded — it makes CPU-bound code single-threaded. I/O-bound code releases the GIL during waits. Most web frameworks (Django, FastAPI) are I/O-bound, so the GIL is rarely the bottleneck. Data pipelines and numerical code should use `multiprocessing`, `concurrent.futures.ProcessPoolExecutor`, or NumPy.
- When the GIL matters — pure-Python loops over large arrays, CPU-bound parsing, image processing without numpy.
- When it doesn't — web servers, database ORMs, REST clients, anything that waits on I/O.
- Python 3.13 free-threaded mode — opt-in PEP 703 build disables the GIL; promising for numerical work but ecosystem still catching up.
- Memory — `__slots__` cuts object size by 40–50%; `sys.getsizeof()` reports instant footprint; `tracemalloc` finds leaks.
Modern Python: 3.11, 3.12, 3.13 highlights
Interviews increasingly test whether you know the modern language features. Mention these if they apply to the question; they signal depth.
- 3.11 — faster CPython (10–60% on pyperformance), `tomllib`, `Self`/`TypeVarTuple`, exception groups.
- 3.12 — `type` statement for type aliases, `ParamSpec` improvements, `f-string` parsing fixes, immutable `frozenset` literal support.
- 3.13 — free-threaded (no-GIL) build, JIT compiler (experimental), `warnings.deprecated`, improved interactive interpreter.
- 3.14 — templated strings (PEP 750), deferred annotation evaluation improvements (PEP 649).
Resources for deeper study
These 25 questions are the core. To go deeper, work through the Python documentation, the PEPs for any feature mentioned, and at least one real project that exercises concurrency and typing.
- Books — "Fluent Python" (2nd edition) by Luciano Ramalho, "Python Cookbook" by David Beazley, "Effective Python" by Brett Slatkin.
- Real Python — the website has tutorials on every topic here, updated for each Python release.
- Practice — LeetCode for algorithm questions, Exercism for idiomatic Python, Advent of Code for end-to-end problem solving.
- Communities — r/learnpython, r/python, PySlackers Slack, the Python Discord.
Common gotchas and interview traps
These come up across all25 questions; know them cold.
- Late binding in closures — Python closures capture variables by reference, so loops that create lambdas all see the final value. Fix with a factory function or default argument.
- Mutable default arguments — `def f(x=[])` shares the list across calls. Use `None` and assign a fresh list inside.
- `is` vs `==` for strings — CPython interns short strings, so `is` may coincidentally work. Use `==` for correctness.
- Float precision — `0.1 + 0.2 != 0.3` because of binary float. Use `decimal.Decimal` for money.
- Generator exhaustion — generators are single-use. Call `list()` once and cache, or use a function returning a new generator.






