Python mock interview with an AI interviewer

A voice mock interview for Python developers preparing for junior, middle or senior roles. The AI interviewer asks the same kinds of Python interview questions real teams ask, from mutability and generators to the GIL, asyncio and typing, and then scores each answer on technical accuracy, completeness and clarity. Pick Python as your technology or upload your resume or a job description to get questions that match the role.

Start Python mock interviewFirst interview of up to 5 questions is free. No card needed.

What the Python interview covers

Data model and mutability

Mutable vs immutable types, identity vs equality, hashability and dunder methods like __eq__, __hash__ and __repr__. Expect follow-ups on mutable default arguments and shallow vs deep copies.

Iterators and generators

The iterator protocol, lazy evaluation with yield and generator expressions, yield from and when a generator beats building a list in memory.

Decorators and context managers

Closures, decorators with arguments, functools.wraps, and resource handling with __enter__/__exit__ or contextlib.contextmanager.

Concurrency, the GIL and asyncio

When to use threading, multiprocessing or asyncio, what the GIL actually blocks, and what the free-threaded builds (experimental in 3.13, officially supported since 3.14) change.

Memory management

Reference counting, the cyclic garbage collector, common leak patterns, __slots__ and profiling memory with tracemalloc.

Type hints

Generics with the 3.12+ type parameter syntax, Protocol vs ABC, TypedDict, and how mypy or pyright fit into a codebase without slowing the team down.

Tooling, packaging and testing

pyproject.toml, virtual environments, uv, ruff and lock files, plus pytest fixtures, parametrization and mocking at the right boundary.

Django and FastAPI

ORM query patterns and the N+1 problem in Django, dependency injection and Pydantic validation in FastAPI, and sync vs async request handling.

Python interview questions by level

Junior

  1. What is the difference between a list and a tuple, and why can a tuple be a dictionary key?

    What a strong answer covers:A list is mutable, a tuple is immutable, and tuples are commonly used for fixed records. Dictionary keys must be hashable, and a tuple is hashable only if all its elements are hashable, so (1, 2) works but a tuple containing a list does not. A list cannot be a key because its contents, and therefore its hash, could change.

  2. What goes wrong with a mutable default argument like def f(items=[])?

    What a strong answer covers:Default values are evaluated once, when the function is defined, so every call without the argument shares the same list and mutations accumulate across calls. The standard fix is to default to None and create a new list inside the function. A strong answer explains the why (definition-time evaluation), not just the workaround.

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

    What a strong answer covers:== compares values via __eq__, while is checks whether two names point to the same object. Use is for singletons like None, and == for everything else. Mention that small ints and some strings may be cached by CPython, so is can appear to work on them, but relying on that is a bug.

  4. What do *args and **kwargs mean in a function signature?

    What a strong answer covers:*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. Bonus points for mentioning keyword-only parameters after * and positional-only parameters before /.

  5. What is the difference between a list comprehension and a generator expression?

    What a strong answer covers:A list comprehension builds the whole list in memory immediately, while a generator expression produces items lazily, one at a time. Generators save memory for large or infinite sequences but can be iterated only once. Use a list when you need indexing, length or multiple passes.

Middle

  1. How does a decorator work, and how do you write one that takes arguments?

    What a strong answer covers:A decorator is a callable that takes a function and returns a replacement, usually a closure that wraps the original; @dec is just f = dec(f). A decorator with arguments is a factory: an outer function takes the arguments and returns the actual decorator. Use functools.wraps so the wrapper keeps the original name, docstring and signature metadata.

  2. Explain the iterator protocol and how generators implement it.

    What a strong answer covers:An iterable has __iter__ returning an iterator; an iterator has __next__ and raises StopIteration when exhausted. A generator function returns a generator object that implements both, pausing its state at each yield. Mention yield from for delegating to a sub-generator and that a for loop simply calls iter() and next() under the hood.

  3. How do context managers work, and when would you write your own?

    What a strong answer covers:The with statement calls __enter__ on entry and __exit__ on exit, even if an exception is raised; __exit__ receives the exception details and can suppress it by returning True. contextlib.contextmanager lets you write one as a generator with a single yield inside try/finally. Typical uses are locks, transactions, temp files and timing blocks.

  4. When would you choose threading, multiprocessing or asyncio?

    What a strong answer covers:In the default CPython build, the GIL lets only one thread run Python bytecode at a time, so threads help with I/O-bound work but not CPU-bound work. multiprocessing sidesteps the GIL with separate processes at the cost of memory and serialization overhead. asyncio handles many concurrent I/O tasks in a single thread but requires async-aware libraries, and any blocking call stalls the whole event loop.

  5. How do you structure tests with pytest for code that calls an external API?

    What a strong answer covers:Isolate the API behind a small client class or function and mock at that boundary, not deep inside third-party libraries. Use fixtures for setup and teardown, parametrize to cover edge cases without copy-paste, and monkeypatch or unittest.mock for the client. Keep a few integration tests against a sandbox or recorded responses to catch contract changes.

Senior

  1. What is the state of the GIL today, and what changes with free-threaded Python?

    What a strong answer covers:PEP 703 added an optional free-threaded build without the GIL; it was experimental in 3.13 and became officially supported, but still not the default, in 3.14. It lets CPU-bound threads run in parallel, but single-threaded code can be somewhat slower and C extensions must be rebuilt and declare support, otherwise the GIL may be re-enabled at runtime. A strong answer also notes that removing the GIL does not make your own code thread-safe: shared mutable state still needs locks.

  2. How does CPython manage memory, and how would you track down a memory leak?

    What a strong answer covers:CPython frees most objects immediately through reference counting and uses a cyclic garbage collector for reference cycles that refcounting cannot reclaim. Leaks in practice usually come from objects kept alive by global caches, unbounded lru_cache, lingering references in closures or listeners, or C extensions. Use tracemalloc snapshots to compare allocations over time, plus tools like objgraph or memray, and consider __slots__ or weakref for large numbers of small objects.

  3. What are descriptors, and how do property and methods rely on them?

    What a strong answer covers:A descriptor is an object defining __get__, and optionally __set__ or __delete__, stored as a class attribute. Data descriptors take precedence over the instance __dict__, non-data descriptors do not, which is why property can intercept assignment while a plain method can be shadowed. Functions are non-data descriptors, and their __get__ is what produces bound methods.

  4. How do you avoid blocking the event loop in an asyncio service, and how do you manage task lifetimes?

    What a strong answer covers:Use async-native clients for I/O and push unavoidable blocking or CPU-heavy work to asyncio.to_thread or a process pool via run_in_executor. Prefer asyncio.TaskGroup (3.11+) for structured concurrency so failures cancel sibling tasks and nothing is left running unobserved. Handle cancellation correctly: clean up in finally and do not swallow CancelledError; add timeouts with asyncio.timeout.

  5. When would you use typing.Protocol instead of an abstract base class?

    What a strong answer covers:An ABC uses nominal typing: classes must explicitly inherit from it, and it can enforce abstract methods at instantiation. A Protocol uses structural typing: any class with matching methods satisfies it for the type checker, which suits third-party types and loose coupling. Protocols are checked statically by default; runtime_checkable enables isinstance checks but only verifies that methods exist, not their signatures.

How the Python mock interview works

  1. Pick Python (and any other technologies from your stack), your level and the interview language: English, Ukrainian or Russian. Or upload your resume and a job description.
  2. Answer each question out loud, the way you would with a real interviewer.
  3. Get a score for every answer on technical accuracy, completeness and clarity, plus a report with your weak spots.

How to answer Python questions out loud

  • →Lead with a one-sentence definition, then explain the mechanism, then give a tiny example. Interviewers score structure as much as facts.
  • →Name the Python version when it matters: dict insertion order (3.7+), match statements (3.10+), TaskGroup (3.11+), free-threaded builds (3.13+). It shows you track the language.
  • →Describe code in words instead of dictating syntax: say "a decorator that wraps the function and retries on failure", not every bracket and colon.
  • →Always state the trade-off: threads vs processes, generators vs lists, ABC vs Protocol. Saying when you would not use something is a strong signal.
  • →Tie answers to real work: a leak you found with tracemalloc, an N+1 query you fixed in Django, a flaky test you stabilized. Concrete beats textbook.

FAQ

Do I write code during the Python mock interview?

No. Questions are verbal and you answer by voice, there is no live coding editor. You explain concepts, trade-offs and how you would approach a problem, the same way you would in a technical screening call.

Can I practice Django or FastAPI together with Python?

Yes. Django and FastAPI can be selected as technologies alongside Python, so the interview mixes core language questions with framework questions. You can also upload a job description to match a specific role.

Is it suitable for experienced Python developers?

Yes. Choose the senior level to get questions on the GIL and free-threading, memory management, descriptors, async architecture and typing. Each answer is scored on technical accuracy, completeness and clarity.

How much does it cost?

Your first interview, up to 5 questions, is free. After that you pay per interview from $1.75, with no subscription. One interview can have up to 50 questions.

Start Python mock interviewFirst interview of up to 5 questions is free. No card needed.

Related mock interviews