Top 40 Python Interview Questions and Answers (2026 Edition)

Preparing for a Python backend interview? These are the 40 questions that come up most often — each with a crisp, interview-ready answer. Every answer links to a full deep-dive guide in this series when you want the code, the follow-up traps, and the trade-offs.

How to use this: read the question, say your answer out loud, then check. Interviewers score decision rules and failure modes, not definitions — "which pattern, what breaks, and what's the complexity?"

Scope note: this set targets Python backend interviews — DSA patterns, the language's data model, async/concurrency, and production Python. Preparing for data-science or ML roles instead? Start with Data Science Interview Questions and Machine Learning Interview Questions.

Coding Patterns & DSA

1. How would you find two numbers that add up to a target (Two Sum)?

Answer: the decision rule is: need complements in one pass → hash map. Store each value's index as you go; for each new number, check whether target - x is already in the map. O(n) time, O(n) space. The trap: sorting + two pointers also works, but preserving the original indices requires carrying them through the sort — sort (value, original_index) pairs — and costs O(n log n); the hash map gives expected O(n). Deep dive →

2. Longest substring without repeating characters — what's the pattern?

Answer: sliding window with a hash set: expand the right edge, and when a duplicate appears, shrink from the left until the window is valid again. O(n) time because each character enters and leaves the window once. The trap: a brute-force "check every substring" is O(n²) and dies on long inputs — the window is what makes it linear. Deep dive →

3. How do you find the top K frequent elements?

Answer: count with collections.Counter, then pick the top K — either most_common(k) or a min-heap of size K. After counting frequencies, a size-k heap avoids sorting all u unique values: O(u log k) selection instead of O(u log u). The trap: the Counter itself may hold up to O(u) unique elements, so overall memory isn't O(k) — the heap only bounds the selection step. That's the complexity distinction interviewers probe. Deep dive →

4. How would you group anagrams together?

Answer: hash map with a signature key: sort each word's letters (or count letter frequencies) to build a canonical key, and bucket words by it. O(n · k log k) with sorting, or O(n · k) with counting. The trap: comparing every pair of words is O(n²) — the signature key turns "are these anagrams?" into a single dict lookup. Deep dive →

5. How do you check whether parentheses are balanced (Valid Parentheses)?

Answer: stack: push opening brackets, pop on closing brackets and verify the match; the string is valid only if the stack ends empty. O(n) time and space. The trap: counting opens vs closes without a stack accepts ")(" — order matters, and only a stack tracks nesting. Deep dive →

6. How do you search in a rotated sorted array?

Answer: still binary search, with one extra decision: at each midpoint, determine which half is properly sorted, then check whether the target lies in that half. O(log n) time. The trap: the classic "is arr[mid] < target?" comparison breaks because the array isn't globally sorted — you must first find the sorted half. Deep dive →

7. How would you count islands in a grid (Number of Islands)?

Answer: DFS/BFS flood fill: scan every cell; when you hit unvisited land, flood-fill the whole island and increment the count. O(m·n) time, visiting each cell once. The trap: forgetting to mark cells visited causes infinite recursion or double-counting — mark on entry. And the Python-specific trap: CPython has a relatively low recursion limit by default, so deep recursion can raise RecursionError — an explicit stack (iterative DFS) is safer for potentially deep input. Deep dive →

8. Climbing Stairs / House Robber — how do you spot the DP?

Answer: the decision rule: the problem asks for an optimum over sequential choices with overlapping subproblems → dynamic programming. Define the recurrence first (e.g., dp[i] = max(dp[i-1], dp[i-2] + nums[i]) for House Robber), then compress to O(1) space with two variables. The trap: the naive recursion is exponential — memoization or bottom-up tabulation is the whole point, and interviewers want to hear you say "overlapping subproblems" out loud. Deep dive →

9. How do you merge overlapping intervals?

Answer: sort by start time, then sweep once, merging each interval into the running one when they overlap. O(n log n) dominated by the sort. The trap: trying to merge without sorting first leads to missed overlaps — sorting is what makes the single pass correct. Deep dive →

10. Container With Most Water — why two pointers?

Answer: two pointers from both ends, always moving the shorter side inward: the area is limited by the shorter line, so moving the taller side can never improve it. O(n) time, O(1) space. The trap: the O(n²) all-pairs check is the obvious brute force — the "move the shorter pointer" insight is the entire interview. Deep dive →

11. How would you design an LRU Cache?

Answer: hash map + doubly linked list: the dict gives O(1) lookup, the list tracks recency — move accessed nodes to the front, evict from the back. Both get and put are O(1). The Python-specific note: collections.OrderedDict (move_to_end/popitem) is another real implementation tool — but for the interview, implement the underlying dict + doubly linked list to demonstrate the data-structure reasoning. And functools.lru_cache is related but different: it caches function results, not the arbitrary key-value cache API this question asks you to design. Deep dive →

12. How do you find the Kth largest element without sorting everything?

Answer: min-heap of size K: push elements, pop whenever the heap exceeds K — the root is your answer. O(n log k) time, O(k) space. The trap: full sort is O(n log n) and fine for small inputs, but the heap wins on streams and huge arrays where K is small — and "quickselect" (average O(n)) is the follow-up they probe next. Deep dive →

Python Language & Data Model

13. What's wrong with def f(x, cache=[])?

Answer: Python's most famous trap: the default list is created once at function definition time, so every call shares the same list and mutations accumulate across calls. The fix is the None sentinel — def f(x, cache=None), then create the list inside. The trap answer they want: "defaults are evaluated at definition time, not call time." Deep dive →

14. Explain the GIL — and when does it actually matter in 2026?

Answer: in the traditional/default CPython runtime, the Global Interpreter Lock means only one thread executes Python bytecode at a time — that's why threads generally don't scale CPU-bound pure-Python work across cores, while they're still useful for I/O concurrency (threads release the GIL while waiting on I/O). Modern CPython also has free-threaded builds where the GIL can be disabled, so state which runtime/build you're discussing — don't present the GIL as an unconditional property of all Python execution. And don't reduce CPU parallelism to "use multiprocessing": the options include process pools, native libraries that release the GIL, free-threaded Python where appropriate, and distributing the work. The decision starts with the workload. Deep dive →

15. Write a decorator that takes arguments — what breaks without functools.wraps?

Answer: a decorator factory is three nested functions: retry(times) returns the decorator, which returns the wrapper. Without @functools.wraps(func), the decorated function presents the wrapper's metadata and can break introspection and tooling that expects the original function — wraps updates the wrapper's metadata and establishes __wrapped__ so inspection tools can recover the original callable and signature. The trap: candidates write the nesting correctly but forget wraps; interviewers treat that as the real test. Deep dive →

16. dataclass vs regular class vs namedtuple vs Pydantic model — when do you use each?

Answer: @dataclass generates __init__, __repr__, and equality for you — reach for it for data-carrying classes; frozen=True gives immutability, and field(default_factory=list) is how you dodge the mutable-default trap (straight back to Q13). A regular class when you need custom behavior and invariants; a namedtuple for tiny immutable records; a Pydantic model when you need validation and parsing at system boundaries. Decision rule: dataclasses for internal data carriers, Pydantic where untrusted input enters. Deep dive →

17. What are dunder methods — and __repr__ vs __str__?

Answer: dunders (double-underscore methods like __eq__, __len__) let your objects plug into Python's syntax and builtins. __repr__ should be unambiguous and developer-oriented — when practical, a constructor-like representation is useful, but that's a convention, not a requirement. __str__ is the human-friendly one. The trap: defining __eq__ without thinking about __hash__ — Python sets __hash__ to None when you define __eq__, silently breaking dict/set usage. Deep dive →

18. Why is is different from ==?

Answer: == tests value equality (via __eq__); is tests identity — whether two names point to the same object in memory. Use == for value equality; use is when identity itself is what you mean — most commonly is None. The trap: never rely on implementation interning or caching for value comparison — code that "works" for small ints and breaks for large ones is the classic bug. Don't memorize interning rules; just never depend on them. Deep dive →

19. Are Python type hints enforced at runtime?

Answer: no — Python remains dynamically typed; annotations are metadata consumed by static type checkers, IDEs, frameworks, and runtime validation libraries. The follow-ups interviewers probe: list[str] (parameterized generics), str | None (union syntax for optional), and Protocol — structural typing that gives you duck typing with static checking: "if it has these methods, it qualifies." Deep dive →

20. Instance method vs @classmethod vs @staticmethod — what's the real difference?

Answer: an instance method takes self and operates on the instance. A classmethod takes cls and operates on the class — the standard tool for alternative constructors (from_dict, from_json). A staticmethod takes neither — just a plain function namespaced under the class. The trap: using a staticmethod where a classmethod belongs breaks subclassing of alternative constructors, because the hardcoded class name won't follow the subclass. Deep dive →

21. What problem does a context manager solve — and how does with work?

Answer: with open(...) as f: guarantees deterministic setup and cleanup — the file closes even if the block raises. The protocol: __enter__ acquires and returns the resource, __exit__ releases it (and can suppress exceptions). For quick ones, @contextlib.contextmanager turns a generator into a context manager. The interview signal: reaching for with whenever you acquire something that must be released — files, locks, connections, temporary state. Deep dive →

22. When do else and finally execute — and what should you catch?

Answer: in try/except/else/finally: else runs only if no exception occurred; finally always runs. The more important half is judgment: catch specific exceptions you can meaningfully handle — don't blanket-catch Exception just to suppress failures, because you'll hide bugs and make debugging miserable. Let what you can't handle propagate. Deep dive →

Iterators, Generators & Functional Patterns

23. What's the difference between an iterable and an iterator?

Answer: the iterator protocol is two dunders: __iter__ returns the iterator object itself, __next__ returns the next item or raises StopIteration — that's the entire contract behind for loops. An iterable is reusable and defines __iter__ (lists, dicts); an iterator is single-pass and defines __next__ (generators, files). The trap: confusing the two — "can I loop over this twice?" is really asking "is it an iterable or an exhausted iterator?" Deep dive →

24. What makes generators special — are they faster?

Answer: a generator (a function with yield) is the easiest way to create an iterator — lazy, producing items one at a time instead of building a list. The classic use is processing data too large to fit in memory. The trap: "generators are faster" is wrong in general — they're lazier; the win is memory, and you can only iterate them once. Deep dive →

25. List comprehension vs generator expression — scope, evaluation, and memory?

Answer: [x for x in data] builds an eager list; (x for x in data) builds a lazy generator that computes nothing until iterated. In Python 3, comprehensions have their own scope, so the loop variable doesn't leak (unlike Python 2) — know that as the scope trap. The memory trap: passing a generator where a list is expected and iterating it twice — the second pass silently yields nothing. Deep dive →

26. What do *args and **kwargs actually do?

Answer: *args collects extra positional arguments into a tuple; **kwargs collects extra keyword arguments into a dict. They're the mechanism behind flexible APIs and decorator wrappers (def wrapper(*args, **kwargs)). The trap: the names args/kwargs are pure convention — it's the * and ** that do the work; also know the reverse (unpacking at call time: f(*lst)). Deep dive →

Concurrency & Async

27. asyncio vs threads vs processes — how do you choose?

Answer: decide from the workload: many I/O operations with synchronous libraries → threads; many concurrent I/O operations with async-native libraries → asyncio; CPU-bound Python → processes / native parallelism / free-threaded where appropriate; mixed workload → separate the concurrency concerns. The trap: picking asyncio while using a blocking database driver — you pay the complexity cost of async and get the throughput of synchronous. Deep dive →

28. What does async/await actually do?

Answer: async def defines a coroutine — a function that can pause. await yields control back to the event loop while waiting, letting other coroutines run. It's single-threaded cooperative concurrency: nothing runs truly in parallel; coroutines interleave at await points. The trap: an async function with no awaits is just overhead — and calling a coroutine without await returns a coroutine object that never runs. Deep dive →

29. What happens if you call blocking code inside an async function?

Answer: you block the event-loop thread — every coroutine on that loop stalls until the blocking call returns. One time.sleep() or one blocking database/client call inside an async request handler freezes all unrelated requests sharing the loop. That's why async code demands async-native libraries for anything that waits — or explicit offloading to threads. This is a far more practical interview question than most syntax trivia. Deep dive →

30. How do you run CPU-bound or blocking work from async code?

Answer: offload it and keep the event loop for coordination: loop.run_in_executor (or anyio.to_thread) pushes blocking I/O to a thread pool; a process pool handles CPU-bound work. The trap: spawning unbounded threads from async code — bound the executor, or your "non-blocking" service dies from thread exhaustion instead. Deep dive →

31. How do threads communicate and share state safely?

Answer: use the right primitive: threading.Lock around compound operations on shared state, queue.Queue for thread-safe handoffs between producers and consumers. For processes there's no shared memory by default — communicate via queues/pipes and serialized messages instead. The trap: assuming "it's just one line" is safe (see next question). Deep dive →

32. Does the GIL prevent race conditions?

Answer: no — the GIL does not make operations atomic. A check-then-act sequence (if key not in d: d[key] = compute()) can still interleave between threads and race. The GIL protects the interpreter's internals, not your invariants. The rule: put a lock around compound operations on shared state, GIL or no GIL. Deep dive →

Testing, Typing & Production Python

33. In pytest, when do you use a fixture vs a mock — and what should you actually mock?

Answer: a fixture is reusable setup — dependency injection for tests (a test database, a sample payload). A mock replaces a collaborator you deliberately don't want to exercise: the network, the clock, a payment gateway. Mock at the boundary — the seam between your code and the outside world — and don't mock what you don't own or the logic you're actually testing. Over-mocked tests verify the mock, not the code. Deep dive →

34. How does Python manage memory — and what causes memory leaks?

Answer: reference counting frees objects the moment their count hits zero; a cyclic garbage collector reclaims reference cycles. Leaks come from references that never die: unbounded caches and lists, globals and listeners that accumulate, cycles involving __del__. Investigate with the gc module (gc.get_objects, gc.collect), tracemalloc for allocation hotspots, and objgraph for what's holding what. The standard fix for caches that shouldn't pin their values: weakref. Deep dive →

35. What is a virtual environment — and why do you pin dependencies?

Answer: a venv isolates a project's dependencies from the system and from other projects — no more "it worked on my machine." Pinning (pip freeze, pip-tools, uv lock) makes the environment reproducible: the same dependency tree in dev, CI, and production. The trap: deploying from unpinned requirements and debugging a transitive-dependency upgrade at 2 AM. Deep dive →

36. Your Python service is slow in production — where do you start?

Answer: profile before guessing: py-spy or cProfile for CPU hotspots — and first classify the bottleneck: CPU-bound, blocked on I/O, or waiting on a lock. Correlate with metrics and structured logs, then reproduce the hot path in a benchmark before optimizing. The interview signal: a methodical elimination, not "I'd rewrite it in a faster language." Deep dive →

37. Why does production Python use the logging module instead of print?

Answer: levels (debug/info/warning/error) let you filter by severity; handlers route output to files, aggregators, and alerting; structured formatting makes logs greppable and correlatable. print has none of that — no levels, no routing, and interleaved output under concurrency. The trap: print-debugging your way through a production incident at 3 AM. Deep dive →

Practical Gotchas

38. What does [lambda: i for i in range(3)] produce — and why [2, 2, 2]?

Answer: calling them gives [2, 2, 2], not [0, 1, 2] — each lambda closes over the variable i, not its value at creation time (late binding), and by call time i is 2 for all of them. The fix freezes the value with a default argument: lambda i=i: i. The trap: this bites in real code with callbacks and threads, not just puzzles — always bind loop variables you close over. Deep dive →

39. What happens with a = b = [] — the mutable aliasing trap?

Answer: both names point to the same list object, so a.append(1) also changes b — assignment never copies in Python. The same trap appears when passing a list into a function and mutating it. The fix: be explicit — b = a.copy() or list(a) when you want independence. The trap answer they want: "names are references, not boxes." Deep dive →

40. Shallow copy vs deep copy — when does it bite?

Answer: a shallow copy copies the outer container but shares the inner objects; deep copy (copy.deepcopy) recursively copies everything. It bites with nested structures — new = old[:] on a list of lists still aliases the inner lists. The trap: "I copied it, why did the original change?" — always ask how deep the nesting goes before choosing. Deep dive →

In this series

  1. Top 40 Python Interview Questions and Answers (2026 Edition) (this post) — start here.
  2. Python Coding Interview Patterns: Lists, Strings & Hash Maps — the essential patterns.
  3. Python Internals for Interviews: Decorators, Generators, GIL & OOP — how Python really works.
  4. Tricky Python Questions Interviewers Love — gotchas and edge cases.
  5. Top 50 Python DSA Problems by Pattern (With Solutions) — 50 problems, solved by pattern.
  6. 50 Python DSA Interview Questions & Answers — Solved by Pattern — the companion drill set.

Related: System Design Interviews: A Practical Primer — the 4-step framework these Python questions plug into.

Comments

Popular posts from this blog

JSP Servlet Interview Questions For Freshers Series 1

Java Banking Finance Services and Insurance (BFSI) domain interview questions

Java program to check even or odd number