Turning Learners Into Developers
Codekilla
CODEKILLA
Python

Python Interview Questions and Answers (2026 Edition)

Top Python interview questions — data structures, OOP, concurrency, type hints, async, performance — with model answers.

← Interview Questions

Python Fundamentals

1. List vs Tuple — when do you use each?

List is mutable, tuple is immutable. Use tuples for fixed-size records (returning multiple values, dict keys, hashable sets). Lists for growable sequences.

2. is vs == ?

is checks identity (same object in memory). == checks equality (same value). Use == for value comparisons; reserve is for singletons like None, True, False.

3. Mutable default argument — what's the pitfall?

python
def add(x, lst=[]):   # BAD — list shared across calls
    lst.append(x); return lst

Default args are evaluated once at definition time. Fix:

python
def add(x, lst=None):
    if lst is None: lst = []
    ...

4. What is the GIL?

The Global Interpreter Lock ensures only one thread executes Python bytecode at a time inside CPython. It simplifies the C implementation but limits CPU-bound multithreading. Workarounds: multiprocessing, asyncio (I/O-bound), or native extensions that release the GIL (NumPy).

5. *args and **kwargs?

*args collects positional args into a tuple; **kwargs collects keyword args into a dict. Spread the other way with func(*lst, **dct).

OOP

6. Difference between class method, static method, instance method?

  • Instance (def m(self)) — receives the instance.
  • Class method (@classmethod) — receives the class. Used for alternative constructors.
  • Static method (@staticmethod) — no implicit first arg. Pure helper that just lives in the class namespace.

7. What is duck typing?

"If it walks like a duck and quacks like a duck, it's a duck." Python doesn't care about declared types — only whether an object supports the operations you use on it. This is being replaced/augmented by structural typing via Protocol in modern type hints.

8. What is __slots__ and why use it?

A class attribute that restricts the allowed instance attributes and replaces __dict__ with a static struct — 30-50% less memory per instance. Useful when you'll create millions of small objects.

Data Structures

9. Dict vs defaultdict vs Counter?

  • dict — plain key → value.
  • defaultdict(list) — auto-creates a default when a missing key is accessed.
  • Counter — defaultdict(int) plus most_common(), arithmetic, set ops.

10. List comprehension vs generator expression?

python
[x*x for x in nums]      # list — eager, allocates memory
(x*x for x in nums)      # generator — lazy, one item at a time

For large data, prefer generators to keep memory flat.

11. Implement an LRU cache.

python
from functools import lru_cache

@lru_cache(maxsize=128)
def fib(n): return n if n < 2 else fib(n-1) + fib(n-2)

Behind the scenes: ordered dict that evicts the least recently used key when the cap is hit.

Async and Concurrency

12. asyncio vs threads vs multiprocessing?

  • asyncio — single-threaded cooperative concurrency. Best for I/O-bound (HTTP, DB).
  • Threads — concurrent I/O, but the GIL serialises Python bytecode. Useful for blocking C libraries.
  • Multiprocessing — true parallelism by spawning subprocesses. Best for CPU-bound work.

13. async/await — when does the event loop yield?

Only at await points. A long synchronous block inside an async function blocks the whole loop. Move CPU work into run_in_executor or a separate process.

14. What is a coroutine?

A function that can pause itself with await and resume later. async def f(): … defines one. Calling f() returns a coroutine object; you await it to actually run it.

Type Hints and Modern Python

15. Why use type hints if Python is dynamic?

  • Tools (mypy, pyright) catch bugs before runtime.
  • IDEs offer better autocomplete and refactoring.
  • Hints serve as inline documentation that stays in sync with the code.

16. Protocol vs ABC?

  • ABC — nominal subtyping (must explicitly inherit).
  • Protocol — structural subtyping (duck-typed; matches if shape matches). Closer to Go interfaces / TypeScript.

17. What does @dataclass do?

Generates __init__, __repr__, __eq__ (and optionally __hash__) from declared fields. Use frozen=True for immutable value objects, slots=True for memory wins.

Performance

18. How do you profile slow Python code?

  • cProfile for function-level timing.
  • line_profiler for line-by-line.
  • py-spy for production sampling profiler (no code changes).
  • memory_profiler / tracemalloc for memory.

19. NumPy vs pure Python for arithmetic?

NumPy operates on contiguous C arrays and offloads loops to optimised C/BLAS — 10-100× faster than equivalent pure-Python loops. Always reach for NumPy when you're doing math on large arrays.

20. What is the "Python is slow" trade-off?

Pure CPython is ~50× slower than C for tight loops. You pay this for productivity. Mitigations: numpy, numba, cython, mypyc, PyPy (alternative JIT-ing interpreter), or rewrite hot paths in Rust/Go via FFI.

Real-World

21. Walk me through a Django request lifecycle.

  1. WSGI / ASGI server accepts the request.
  2. URL resolver matches the path to a view.
  3. Middleware chain runs (auth, sessions, CSRF, GZip).
  4. View function executes — queries the DB via the ORM, returns a HttpResponse.
  5. Middleware reverses on the way out.
  6. Response is serialised + sent.

22. Decorator — write @retry(times=3) from memory.

python
import functools, time
def retry(times=3, delay=1):
    def deco(fn):
        @functools.wraps(fn)
        def wrap(*a, **kw):
            for i in range(times):
                try: return fn(*a, **kw)
                except Exception:
                    if i == times - 1: raise
                    time.sleep(delay)
        return wrap
    return deco

23. Tell me about a Python bug you debugged in production.

Strong-answer template: (1) symptom in customer terms, (2) hypothesis you tested first, (3) data you collected (logs, profile, tracebacks), (4) actual root cause, (5) the fix + tests + monitoring to prevent recurrence.

24. Pytest fixtures — what are they?

Reusable setup/teardown helpers. Use @pytest.fixture to declare; tests pull them in by adding the fixture name as a function arg.

25. What is __main__ and if __name__ == "__main__": ?

When Python runs a file directly, it sets __name__ == "__main__". The guard lets the same file be importable as a module without executing the script behaviour.