# Python Interview Questions (2026) with Answers

> 30 Python interview questions from junior to senior, each with what a strong answer shows. Practise, then prove the skill with evidence.

Source: https://getlemma.io/skills/python/interview-questions

Python interviews test whether you understand how the language behaves, not just its syntax. Expect questions on mutability, scoping, iteration, concurrency, typing and testing. The questions below are grouped by level, from junior to senior. Each one says what a strong answer covers and the trap a weak answer falls into.

[How Lemma tests Python](https://getlemma.io/skills/python)

## Junior Python interview questions

1.  ### 1.What is the difference between a list and a tuple, and when would you use each?

    What a strong answer shows

    A list is mutable and suits a collection that grows or changes. A tuple is immutable, so it can be a dict key if its items are hashable, and it signals a fixed record. A weak answer stops at the brackets and misses that a tuple holding a list is not hashable.

2.  ### 2.What is wrong with this function?

    ```
    def add(item, items=[]):
        items.append(item)
        return items

    add(1)  # [1]
    add(2)  # [1, 2], the same list again

    def add(item, items=None):
        if items is None:
            items = []
        items.append(item)
        return items
    ```

    What a strong answer shows

    Default values are evaluated once, when the function is defined, so every call without the argument shares the same list. The fix is a None default and a new list inside the function. A strong answer explains the cause rather than just reciting the fix.

3.  ### 3.What is the difference between is and ==?

    What a strong answer shows

    \== compares values through \_\_eq\_\_, while is checks whether two names refer to the same object, so use is for singletons such as None. Small integers and some strings look identical under is only because CPython caches them. A strong answer treats that as an implementation detail and never relies on it.

4.  ### 4.Does Python pass arguments by value or by reference?

    What a strong answer shows

    Neither label fits: the function receives a reference to the same object. Mutating that object, such as appending to a list, is visible to the caller, but rebinding the parameter name is not. A weak answer picks one label and cannot explain both behaviours.

5.  ### 5.What is the difference between a shallow copy and a deep copy?

    ```
    import copy

    grid = [[0, 0], [0, 0]]
    shallow = grid.copy()
    deep = copy.deepcopy(grid)

    grid[0][0] = 1
    print(shallow[0][0])  # 1, inner list is shared
    print(deep[0][0])     # 0
    ```

    What a strong answer shows

    A shallow copy creates a new container holding references to the same inner objects, so nested lists or dicts are still shared. copy.deepcopy recursively copies nested objects as well. A strong answer mentions that slicing, list() and dict.copy() are all shallow.

6.  ### 6.What do \*args and \*\*kwargs do?

    What a strong answer shows

    \*args collects extra positional arguments into a tuple and \*\*kwargs collects extra keyword arguments into a dict. The same operators unpack sequences and mappings at a call site. A strong answer gives a real use, such as a wrapper that forwards arguments it does not need to know about.

7.  ### 7.When would you use a list comprehension instead of a for loop?

    What a strong answer shows

    A comprehension suits building a new collection from an existing iterable with a simple transform or filter, and it reads as one expression. A plain loop is clearer when there are side effects, several branches or nested logic. A weak answer uses comprehensions only for their side effects, building a list nobody reads.

8.  ### 8.What can be used as a dictionary key, and why?

    What a strong answer shows

    Keys must be hashable: their hash must never change while they are in the dict, and objects that compare equal must have equal hashes. That rules out lists, dicts and sets, while ints, strings, frozensets and tuples of hashable items work. A strong answer connects this to how a dict finds a key by its hash.

9.  ### 9.What do the else and finally blocks in a try statement do?

    What a strong answer shows

    else runs only if the try block raised nothing, which keeps code that should not be guarded out of the try. finally always runs, even after a return or an exception, so it suits clean-up. A strong answer also warns against a bare except, which swallows KeyboardInterrupt and real bugs.

10.  ### 10.Why use a virtual environment, and how do you manage a project's dependencies?

    What a strong answer shows

    A virtual environment isolates a project's packages from the system Python and from other projects, so versions do not clash. Dependencies are declared in pyproject.toml and pinned in a lock file so every machine installs the same versions. A weak answer installs packages globally and cannot reproduce a colleague's environment.

## Mid-level Python interview questions

1.  ### 11.Why does this code print 2, 2, 2 instead of 0, 1, 2?

    ```
    funcs = [lambda: i for i in range(3)]
    print([f() for f in funcs])  # [2, 2, 2]

    funcs = [lambda i=i: i for i in range(3)]
    print([f() for f in funcs])  # [0, 1, 2]
    ```

    What a strong answer shows

    Closures capture variables, not values, and the lambdas all look up i after the loop has finished. Binding the current value as a default argument, or using functools.partial, fixes it. A strong answer also explains LEGB name lookup and when nonlocal or global is needed to rebind an outer name.

2.  ### 12.What is a generator, and when would you use one instead of returning a list?

    ```
    def read_rows(path):
        with open(path) as f:
            for line in f:
                yield line.rstrip().split(",")

    total = sum(float(row[2]) for row in read_rows("sales.csv"))
    ```

    What a strong answer shows

    A generator function uses yield to produce values lazily, one at a time, keeping its state between calls. It suits large or endless sequences and pipelines where holding everything in memory is wasteful. A strong answer notes that a generator can be consumed only once, so passing it to two consumers is a common bug.

3.  ### 13.What is the difference between an iterable and an iterator?

    What a strong answer shows

    An iterable has \_\_iter\_\_, which returns a fresh iterator; an iterator has \_\_next\_\_ and raises StopIteration when exhausted. A for loop calls iter() once and then next() repeatedly. A strong answer uses this to explain why a list can be looped over twice but a generator cannot.

4.  ### 14.Write a decorator that logs how long a function takes.

    ```
    import functools, time

    def timed(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            start = time.perf_counter()
            try:
                return func(*args, **kwargs)
            finally:
                print(f"{func.__name__} took {time.perf_counter() - start:.3f}s")
        return wrapper
    ```

    What a strong answer shows

    A decorator is a function that takes a function and returns a replacement, usually a wrapper that forwards \*args and \*\*kwargs. A strong answer uses functools.wraps so the wrapped function keeps its name and docstring. It times inside try and finally so failures are measured too.

5.  ### 15.What does a with statement do, and how would you write your own context manager?

    ```
    from contextlib import contextmanager

    @contextmanager
    def transaction(conn):
        try:
            yield conn
            conn.commit()
        except Exception:
            conn.rollback()
            raise
    ```

    What a strong answer shows

    It calls \_\_enter\_\_ on entry and guarantees \_\_exit\_\_ on the way out, including when an exception is raised. You write one as a class with those two methods, or as a generator decorated with contextlib.contextmanager. A strong answer knows that \_\_exit\_\_ returning a true value suppresses the exception, and that this is rarely wanted.

6.  ### 16.What are dunder methods? Explain \_\_repr\_\_ versus \_\_str\_\_.

    What a strong answer shows

    Dunder methods let objects work with built-in syntax such as len(), ==, iteration and +. \_\_repr\_\_ is an unambiguous form for developers and logs, while \_\_str\_\_ is for end users and falls back to \_\_repr\_\_. A strong answer adds that defining \_\_eq\_\_ without \_\_hash\_\_ makes instances unhashable.

7.  ### 17.When would you use a dataclass instead of a regular class?

    What a strong answer shows

    A dataclass generates \_\_init\_\_, \_\_repr\_\_ and \_\_eq\_\_ from annotated fields, which removes boilerplate for classes that mainly hold data. frozen=True makes instances immutable and hashable, and slots=True reduces memory. A weak answer uses a mutable default such as a list instead of field(default\_factory=list).

8.  ### 18.What is the difference between a classmethod and a staticmethod?

    What a strong answer shows

    A classmethod receives the class as its first argument, so it suits alternative constructors that should also work for subclasses. A staticmethod receives nothing implicit and is just a function grouped with the class. A strong answer gives the from\_dict constructor as an example and says a module-level function is often the better choice than a staticmethod.

9.  ### 19.How does a dict work internally, and what is the cost of a lookup?

    What a strong answer shows

    A dict is a hash table: the key's hash picks a slot and equality resolves collisions, so lookups average O(1). Since Python 3.7, dicts preserve insertion order as a language guarantee. A strong answer explains why a key whose hash changes after insertion becomes impossible to find.

10.  ### 20.What do type hints do at runtime, and how do you use them in a project?

    What a strong answer shows

    The interpreter does not enforce them; they are checked by tools such as mypy or Pyright and used by editors. A strong answer runs the checker in CI, annotates public interfaces first, and uses Optional or X | None explicitly. It also knows libraries such as Pydantic read hints at runtime to validate data.

11.  ### 21.How do you test code that calls an external API?

    ```
    # app/weather.py does: from app.client import fetch_forecast
    from unittest.mock import patch
    from app.weather import umbrella_needed

    def test_umbrella_when_rain_forecast():
        with patch("app.weather.fetch_forecast", return_value={"rain": 0.8}):
            assert umbrella_needed("London")
    ```

    What a strong answer shows

    Isolate the call behind a small function or client, then replace it in tests with a fake or with unittest.mock.patch. Patch the name where it is looked up, not where it is defined, which is the most common mocking mistake. A strong answer uses pytest fixtures for setup and keeps a few integration tests against the real service.

12.  ### 22.When would you use threads, and when would you use processes?

    What a strong answer shows

    In standard CPython the GIL lets one thread run Python bytecode at a time, so threads help I/O-bound work, not CPU-bound work. CPU-bound work needs multiprocessing, or code that releases the lock such as NumPy. A strong answer mentions concurrent.futures as the simplest interface to both.

## Senior Python interview questions

1.  ### 23.How does asyncio work, and what goes wrong when you call blocking code inside a coroutine?

    ```
    import asyncio

    async def fetch_all(client, urls, limit=10):
        sem = asyncio.Semaphore(limit)

        async def one(url):
            async with sem:
                return await client.get(url)

        return await asyncio.gather(*(one(u) for u in urls))
    ```

    What a strong answer shows

    A single-threaded event loop runs coroutines, switching between them only at await points, so a blocking call stalls every task. A strong answer moves blocking work to asyncio.to\_thread or an executor. It also limits concurrency with a semaphore rather than launching every task at once.

2.  ### 24.Does the GIL make Python code thread-safe?

    What a strong answer shows

    No. The GIL protects the interpreter's internals, but a thread can be switched out between bytecodes, so counter += 1 can still race. A strong answer protects shared state with a lock or queue, and knows CPython 3.13 added an optional free-threaded build without the GIL.

3.  ### 25.How does super() work with multiple inheritance?

    ```
    class A:
        def greet(self): return "A"
    class B(A):
        def greet(self): return "B" + super().greet()
    class C(A):
        def greet(self): return "C" + super().greet()
    class D(B, C):
        pass

    print(D().greet())  # BCA, because D.__mro__ is D, B, C, A, object
    ```

    What a strong answer shows

    super() calls the next class in the method resolution order of the instance's type, not necessarily the parent written in the class body. The MRO is computed by C3 linearisation and shown by \_\_mro\_\_. A strong answer explains why cooperative classes must all call super() and accept \*\*kwargs for this to work.

4.  ### 26.How does @property work under the hood?

    What a strong answer shows

    property is a descriptor: an object stored on the class that defines \_\_get\_\_, and optionally \_\_set\_\_ and \_\_delete\_\_. Attribute lookup on an instance checks data descriptors on the class before the instance \_\_dict\_\_. A strong answer connects this to how methods become bound methods and how ORMs define model fields.

5.  ### 27.How does CPython manage memory, and how would you find a memory leak?

    What a strong answer shows

    CPython frees most objects immediately through reference counting, and a cyclic garbage collector handles reference cycles. Leaks in Python are usually objects kept alive by mistake, such as unbounded caches, module-level lists or lingering references in closures. A strong answer uses tracemalloc snapshots to compare allocations over time, rather than guessing.

6.  ### 28.A Python job is too slow. How do you make it faster?

    What a strong answer shows

    Measure first with cProfile or a sampling profiler such as py-spy, and fix the hot path the profile shows. The biggest wins are usually algorithmic: a set instead of a list for membership, batching I/O, or avoiding repeated work. Only then consider vectorising with NumPy, adding concurrency, or moving a hot loop to compiled code.

7.  ### 29.When would you use a Protocol instead of an abstract base class?

    ```
    from typing import Protocol

    class Closeable(Protocol):
        def close(self) -> None: ...

    def shutdown(resources: list[Closeable]) -> None:
        for r in resources:
            r.close()  # any object with close() type-checks
    ```

    What a strong answer shows

    An abstract base class requires explicit inheritance and can enforce abstract methods at instantiation. A Protocol describes structural typing: any object with the right methods satisfies it for the type checker, without inheriting anything. A strong answer prefers Protocols at boundaries, such as dependencies injected for testing, and knows runtime\_checkable only checks method names, not signatures.

8.  ### 30.How would you package and publish an internal Python library?

    What a strong answer shows

    Declare metadata in pyproject.toml with a build backend, use a src layout, and build a wheel in CI. Libraries declare compatible version ranges, while applications pin exact versions in a lock file. A weak answer pins a library's dependencies exactly and causes conflicts for every consumer.

![Painting of an olive grove in soft sunlight with hills beyond](https://cdn.sanity.io/images/islu4peo/production/613eb3f93566423c9adf45246f2cf1a61437c04c-1680x1120.jpg?w=1080&fm=webp&q=68&fit=max)

## Prove it instead of claiming it.

Answers show you can talk about Python. A dated test result and live sessions on a Skills Passport show you can do it. Lemma is in early access.

## Python interviews: common questions

### How do I prepare for a Python coding interview?

Solve problems in plain Python without an IDE's autocomplete, and talk through your reasoning as you go. Know the standard library well, especially collections, itertools and functools. Then review how the language behaves: mutability, scoping, iteration and exceptions come up at every level.

### What Python topics do senior interviews focus on?

Senior interviews move from syntax to judgement. Expect concurrency with threads, processes and asyncio, performance profiling, typing across a codebase, testing strategy and packaging. Interviewers want trade-offs explained with reasons, not a single correct answer.

### Should I write type hints in a Python interview?

Add them to function signatures if they make your intent clearer and they do not slow you down. They show you write code for other people to read. In a timed exercise, a correct solution matters more than complete annotations, so skip them for small helpers if time is short.

## More interview questions

[Browse the skills directory](https://getlemma.io/skills)

-   [SQL interview questionsJunior to senior, with what a strong answer shows](https://getlemma.io/skills/sql/interview-questions)
-   [Product management interview questionsJunior to senior, with what a strong answer shows](https://getlemma.io/skills/product-management/interview-questions)
-   [React interview questionsJunior to senior, with what a strong answer shows](https://getlemma.io/skills/react/interview-questions)
-   [System design interview questionsJunior to senior, with what a strong answer shows](https://getlemma.io/skills/system-design/interview-questions)
-   [JavaScript interview questionsJunior to senior, with what a strong answer shows](https://getlemma.io/skills/javascript/interview-questions)
-   [AWS interview questionsJunior to senior, with what a strong answer shows](https://getlemma.io/skills/aws/interview-questions)
-   [Node.js interview questionsJunior to senior, with what a strong answer shows](https://getlemma.io/skills/nodejs/interview-questions)
-   [TypeScript interview questionsJunior to senior, with what a strong answer shows](https://getlemma.io/skills/typescript/interview-questions)
-   [Docker interview questionsJunior to senior, with what a strong answer shows](https://getlemma.io/skills/docker/interview-questions)

Practising answers is half of it; the other half is showing the skill. See [how Lemma tests Python](https://getlemma.io/skills/python), how [adaptive skills tests](https://getlemma.io/product/assessments) work, or [how interviewers assess technical skills](https://getlemma.io/blog/assess-technical-skills-in-interview).
