Python-specific interview questions covering async programming, type hints, testing, packaging, and idiomatic patterns.
Distinguish I/O-bound from CPU-bound: "For I/O-bound work, asyncio or threading works fine despite the GIL. For CPU-bound work, I use multiprocessing or offload to C extensions that release the GIL."
The GIL ensures only one thread executes Python bytecode at a time, protecting internal state but limiting CPU-bound parallelism in threads. Workarounds: multiprocessing for CPU-bound work (separate processes, each with its own GIL), asyncio for I/O-bound concurrency, C extensions that release the GIL (NumPy), and concurrent.futures for clean process/thread pool abstractions. Strong candidates also mention that the GIL does not affect I/O-bound threading and know about the ongoing efforts to remove it (PEP 703, free-threaded Python 3.13+).
Core Python knowledge. Candidates who do not know about the GIL will make poor concurrency decisions. Those who can discuss both the limitations and the practical workarounds demonstrate depth.
Show a practical adoption strategy: "We added mypy to CI in strict mode for new code and gradually typed existing modules. For untyped libraries, we wrote thin typed wrappers rather than scattering type: ignore everywhere."
Type hints (PEP 484+) add static type annotations without runtime enforcement. Tools: mypy, pyright, or pytype for static checking. Best practices: use typing module types (Optional, Union, List, Dict), type aliases for complex types, Protocol for structural subtyping, and TypeVar for generics. For untyped libraries: use type: ignore comments sparingly, write stub files (.pyi), check typeshed for existing stubs, or use py.typed marker. Strong candidates discuss gradual typing strategy.
Tests modern Python practices. Candidates who dismiss type hints are missing productivity gains. Those who use them idiomatically with mypy in CI demonstrate professional-grade Python development.
Be clear about the trade-off: "asyncio shines for I/O-heavy services like API gateways handling thousands of concurrent requests. For CPU-bound work or simple scripts, it adds complexity without benefit."
asyncio uses cooperative multitasking: a single thread runs an event loop that switches between coroutines at await points. Best for I/O-bound workloads with many concurrent connections (web servers, API clients, database queries). Differs from threading: no GIL overhead, no race conditions from shared mutable state, but all code must be non-blocking — one CPU-bound coroutine blocks everything. Strong candidates discuss when asyncio is overkill, how to integrate sync and async code (run_in_executor), and popular async frameworks (FastAPI, aiohttp).
Tests understanding of Python concurrency models. Candidates who cannot explain the event loop or confuse async with threading will write incorrect concurrent code. Ask about a real project where they chose async (or chose not to).
Show pragmatism: "I use function decorators for cross-cutting concerns like caching and auth. Class decorators for registration patterns. Metaclasses almost never — in 10 years I have needed them twice."
Decorators are functions that take a function/class and return a modified version. Function decorators wrap individual functions (logging, caching, auth checks). Class decorators modify or wrap entire classes (adding methods, registering plugins). Metaclasses control class creation itself — rarely needed, used for ORMs or validation frameworks. Strong candidates use functools.wraps to preserve metadata, know the @property decorator, and can explain when a simple decorator beats a metaclass (almost always).
Tests Python depth. Candidates who cannot write a decorator from scratch may lack understanding of closures and first-class functions. Those who reach for metaclasses when a decorator suffices are over-engineering.
Describe your setup: "I use pytest with fixtures scoped per function for isolation. Parametrize replaces duplicate test methods. I patch external services but prefer dependency injection for internal code."
pytest is the community standard: less boilerplate than unittest, powerful fixtures with dependency injection, parametrize for table-driven tests, and a rich plugin ecosystem. Fixtures replace setUp/tearDown with explicit, composable, scoped resources. Mocking: unittest.mock.patch for replacing dependencies, or dependency injection to avoid patching entirely. Strong candidates discuss test organisation (unit/integration/e2e directories), conftest.py for shared fixtures, and markers for slow tests.
Fundamental Python testing knowledge. Candidates who only know unittest may be behind on ecosystem conventions. Those who can discuss fixture scoping, parametrize, and test organisation demonstrate professional testing practices.
Show current awareness: "I use Poetry for application projects because it handles lockfiles and virtual environments together. For libraries, I use pyproject.toml with Hatch for PEP 621 compliance."
Virtual environments isolate project dependencies. Tools: venv (built-in), pip + requirements.txt (basic), pip-tools (compiled lockfiles), Poetry (dependency resolution + packaging), PDM or Hatch (modern PEP 621). Strong candidates discuss: lockfiles for reproducibility, separating dev/prod dependencies, dependency resolution conflicts, and publishing packages. They should know about pyproject.toml as the modern config standard.
Baseline Python ecosystem knowledge. Candidates who manually manage requirements.txt without lockfiles will cause "works on my machine" problems. Those who know modern tooling (Poetry, PDM, uv) are up to date.
Give a concrete example: "My PagedResults class implements __len__ for total count, __iter__ to yield items across pages, and __getitem__ for index access. Dataclasses handle __init__, __repr__, and __eq__ automatically."
Python's data model lets custom classes integrate with built-in operations by implementing special methods: __len__ for len(), __iter__/__next__ for iteration, __eq__/__lt__/__hash__ for comparison and hashing, __repr__/__str__ for string representation, __getitem__ for indexing, and __enter__/__exit__ for context managers. Strong candidates discuss the total_ordering decorator, the difference between __repr__ and __str__, and dataclasses as a shortcut for common dunder methods.
Tests Python depth beyond surface usage. Candidates who cannot implement basic dunder methods will struggle with Pythonic design. Ask them to sketch a class that works as a context manager — this tests __enter__/__exit__ understanding.
Match to requirements: "For a content-heavy site with admin needs, Django saves weeks. For a high-throughput API service, FastAPI with async gives better performance with less overhead."
Django: batteries-included, ORM, admin, auth, templating — best for full-stack web apps and rapid prototyping. FastAPI: async-first, Pydantic validation, OpenAPI generation, minimal — best for APIs and microservices. Trade-offs: Django is heavier but more productive for traditional web apps; FastAPI is leaner and faster for pure API services. Strong candidates discuss the middleware and ecosystem of each, when Django REST Framework bridges the gap, and how async support in Django 4+ changes the calculus.
Tests practical framework knowledge. Candidates who only know one framework should still articulate its trade-offs. Those who dismiss either without understanding its strengths lack perspective.
Show diagnostic experience: "I use tracemalloc to snapshot memory at two points and compare. For cycle leaks, gc.set_debug(gc.DEBUG_SAVEALL) captures uncollectable objects. objgraph visualises reference chains."
Python uses reference counting as primary memory management — objects are freed when refcount reaches zero. The cyclic garbage collector handles reference cycles (objects that reference each other). Memory leaks in Python: circular references with __del__, caching without bounds, global collections that grow, C extension leaks. Diagnostic tools: tracemalloc (stdlib), objgraph, memory_profiler, gc.get_referrers(). Strong candidates discuss __slots__ for memory reduction and weakref for breaking cycles.
Senior Python question. Candidates who think Python cannot have memory leaks are wrong. Those who can systematically diagnose memory growth using profiling tools demonstrate production experience.
Describe a practical setup: "We use ruff for linting and formatting, mypy in strict mode for new code, and pre-commit hooks so nothing unformatted reaches the repo. CI fails on any violation."
Key tools: ruff or flake8 for linting, black or ruff format for formatting, mypy for type checking, isort for import ordering, pre-commit hooks to run checks automatically, and CI enforcement. Strong candidates discuss: adopting tools incrementally, configuring in pyproject.toml, using pre-commit framework, code review practices, and the balance between strict enforcement and developer experience. Mention ruff as the modern all-in-one replacement.
Tests professional Python practices. Candidates without linting or formatting tools will produce inconsistent code. Those with a complete toolchain in CI demonstrate team-oriented development.