Python Developer Interview Questions and Answers

Python developer interviews in 2026 still start with lists, dictionaries, and functions—but the bar has moved toward production judgment: when to use asyncio instead of threads, how Django ORM queries scale, and whether your FastAPI service loads a model once or on every request. Hiring managers report that strong resumes fail when candidates cannot explain the event loop or defend framework trade-offs under follow-up questions.

Below are 49 questions for Python developer, full stack Python developer, and senior backend loops—from core language and OOP through Django, FastAPI, asyncio, testing, and deployment. For OOP fundamentals and SOLID (language-agnostic with Java emphasis), see OOP interview questions. For dedicated Django interview prep for experienced professionals, see Django interview questions for experienced developers. For Apache Kafka and event pipelines, see Kafka interview questions. For Kubernetes interview questions when deploying containers, see Kubernetes interview questions. For PostgreSQL interview questions with psycopg/SQLAlchemy stacks, see PostgreSQL interview questions. Open each answer after you try the question yourself. For pandas interview questions and DataFrame depth, see pandas interview questions. For broader ML and statistics prep, see data science interview questions. For browser-to-database integration across stacks, see full stack developer interview questions.

NOTE
Prep target: Read the job description for framework emphasis (Django monolith vs FastAPI microservice vs data/ML pipeline). Junior loops weight syntax and small functions; senior loops push concurrency, ORM performance, testing, and system design within Python's constraints.

Interview context and how to prepare

What do Python developer interviews actually test?

Python interviews test whether you can ship and debug real services, not only recite syntax.

Level Typical focus
Junior (0–2 yrs) Types, control flow, comprehensions, basic OOP, file I/O, simple functions
Mid (3–5 yrs) Decorators, generators, error handling, SQL/ORM, pytest, REST API basics
Senior (6+ yrs) GIL and concurrency choice, ORM performance, async services, CI/CD, system design, trade-offs
Full stack Python Above plus frontend integration, auth flows, schema design, deployment

Live coding often includes:

  • List/dict/string manipulation
  • Parsing JSON or CSV
  • Writing a small API handler or data transform
  • Debugging mutable-default or async bugs

A strong answer is:

"Python interviews usually start with language fundamentals, but for experienced roles I expect to connect them to production concerns such as concurrency, database queries, testing, APIs, and debugging."

Python developer vs full stack Python developer — what changes in interviews?

What interviewers are testing: Whether you know full-stack screens add frontend integration and API contracts on top of core Python and framework depth.

A Python developer loop usually emphasizes backend depth: language internals, frameworks, databases, testing, and deployment.

A full stack Python developer loop adds UI integration:

Area Python backend focus Full stack Python add-on
APIs Django REST / FastAPI routes, validation, auth CORS, cookie vs JWT from React/Vue
Architecture Service layers, workers (Celery) End-to-end feature ownership
Interview task CRUD endpoint, query optimization Form + API + DB in one session

Common full stack stacks in 2026:

  • Django + React — batteries-included backend, admin, ORM conventions
  • FastAPI + React/Vue — async APIs, OpenAPI contracts, microservices
  • Flask + SPA — minimal core; you justify extensions (Flask-Login, SQLAlchemy) A strong answer is:

"Full-stack Python screens add frontend integration, API contracts, and deployment; backend screens go deeper on concurrency, ORM, and service design."

What is a typical Python developer interview loop?

Most companies run 4–6 rounds:

Round Duration Focus
Recruiter / HM screen 30 min Background, stack, projects
Core Python 45–60 min Types, OOP, decorators, data structures
Framework deep dive 45–60 min Django ORM, FastAPI async, or Flask patterns
Live coding 45–90 min Algorithms, parsing, API snippet, debugging
System design 45–60 min Services, queues, caching—senior roles
Behavioral 30–45 min Ownership, incidents, mentoring

Take-home assignments may ask for a small REST API with tests and a README—quality of error handling and tests often matters more than feature count.

A strong candidate thinks aloud during live coding and states assumptions before coding.

What is a realistic 4–6 week prep plan?
Week Focus Output
1 Python fundamentals — mutability, collections, functions Explain mutable default trap and collection trade-offs aloud
2 OOP, decorators, generators, context managers Write one decorator and one context manager from memory
3 Framework track (pick JD match) — Django ORM or FastAPI async One CRUD API with validation and tests
4 Django/FastAPI, ORM, testing, API design Add pytest coverage and defend framework choice for the JD
5 Concurrency — threading vs multiprocessing vs asyncio Explain GIL and free-threaded Python trade-offs with one I/O-bound example
6 System design + behavioral STAR stories Sketch notification or ingestion service; rehearse 3 stories

Ship one mini-project (todo API, URL shortener, or ingestion script) with Docker and pytest—not only flashcards.


Python fundamentals and the object model

Explain mutable vs immutable types in Python.

What interviewers are testing: Whether you tie mutability to hashability, default-argument traps, and shared-state bugs—not just list versus dict definitions.

Immutable built-ins include int, float, str, tuple, frozenset, and bytes. A tuple itself is immutable, but it may contain mutable objects; it is hashable only when all its elements are hashable.

Mutable objects can change in place: list, dict, set, most user-defined classes.

Why interviewers care:

Scenario Pitfall
Dict key Keys must be hashable → no mutable lists as keys
Default function args Shared mutable default (see Q6)
Thread safety Mutating shared lists/dicts without locks
Copy semantics Shallow copy shares nested mutable objects
python
s = "hello"
# s[0] = "H"  # TypeError — str is immutable

items = [1, 2]
items.append(3)  # mutates list in place
Output

For a deeper comparison of built-in collections, see list vs set vs tuple vs dictionary.

A strong answer is:

"Mutable types like lists and dicts change in place while strings and tuples do not—I tie that to bugs like shared default arguments and unexpected aliasing."

What is the mutable default argument trap?

What interviewers are testing: Whether you avoid mutable default arguments and explain the shared-list trap across calls.

Default arguments are evaluated once at function definition time—not on each call.

python
def append_item(item, bucket=None):
    if bucket is None:
        bucket = []
    bucket.append(item)
    return bucket

print(append_item(1))  # [1]
print(append_item(2))  # [2] — safe pattern
Output

Broken pattern (common interview trap):

python
def bad_append(item, bucket=[]):
    bucket.append(item)
    return bucket

print(bad_append(1))  # [1]
print(bad_append(2))  # [1, 2] — same list reused!
Output

Fix: use None as sentinel and create a fresh list inside the function.

Interviewers want you to explain object identity (id(bucket) stays same across calls in the bug version).

A strong answer is:

"Default arguments are evaluated once at definition time, so I use None as a sentinel and create a fresh list inside the function to avoid shared mutable state across calls."

When do you use list, tuple, set, or dict?

What interviewers are testing: Whether you pick list, tuple, set, or dict based on ordering, mutability, uniqueness, and lookup needs.

Type Ordered Mutable Duplicates Typical use
list Yes Yes Yes Sequences, stacks, ordered work
tuple Yes No Yes Fixed records, dict keys, return bundles
set No Yes No Membership, dedupe, unique tags
dict Yes* Yes Keys unique Lookup, counting, JSON-like maps

*Dictionaries preserve insertion order as a language guarantee since Python 3.7.

Choose a tuple for fixed-position records—it can be used as a dictionary key only when all contained values are hashable. Use set for O(1) average membership; dict for key→value maps; list when you need order with mutation.

A strong answer is:

"I pick lists for ordered mutable sequences, tuples for fixed records, sets for O(1) membership, and dicts for key-value lookup."

List comprehension vs generator expression — when does each win?

What interviewers are testing: Whether you pick a generator expression for one-pass iteration and a list comprehension when you need a materialized list.

List comprehension — builds full list in memory:

python
squares = [n * n for n in range(1_000_000)]
Output

Generator expression — lazy, constant memory:

python
squares = (n * n for n in range(1_000_000))
Output
Case Prefer
Small data, need indexing/reuse List comprehension
Large files/streams, pipeline Generator
Sum/max/any once sum(n*n for n in range(...))

Interviewers link this to 50GB log processing—you cannot materialize all lines as a list.

See python yield for generator functions with state.

A strong answer is:

"I use a list comprehension when I need the full list in memory and a generator expression for large or streaming data where I only iterate once."

Does Python pass by value or by reference?

What interviewers are testing: Whether you understand that Python passes object references by assignment and can distinguish rebinding a parameter from mutating a shared object.

Python passes arguments by object sharing (often described as "call by sharing" or "pass by assignment"). The function receives another reference to the same object, but rebinding the local parameter does not rebind the caller's variable.

  • Names bind to objects
  • Rebinding a parameter (x = x + 1 on int) does not affect caller
  • Mutating a shared object (lst.append(1)) does affect caller
python
def grow(lst):
    lst.append(99)

data = [1]
grow(data)
print(data)  # [1, 99]
Output

A strong answer is:

"Python passes references to objects by assignment. If I mutate a passed mutable object, the caller can observe that change; if I merely rebind the parameter name, the caller's binding is unchanged."

What is the difference between is and ==?

What interviewers are testing: Whether you use is only for singleton identity checks like None and == for value equality.

  • == compares values (calls __eq__)
  • is compares object identity (id(a) == id(b))

Use is for identity checks, most commonly is None / is not None. Do not use it for string, numeric, or ordinary value comparison. Other genuine singleton identity cases include NotImplemented and Ellipsis.

python
x = None
if x is None:  # idiomatic

enabled = True
if enabled:  # idiomatic — not `if enabled is True`
    ...
Output

A strong answer is:

"I use == for value equality and is only for identity checks like is None—not for comparing strings or numbers."

Shallow copy vs deep copy?

What interviewers are testing: Whether you know shallow copy shares nested mutable objects and deepcopy is required for independent nested structures.

python
import copy

original = [[1], [2]]
shallow = copy.copy(original)
deep = copy.deepcopy(original)

original[0].append(99)
print(shallow[0])  # [1, 99] — nested list shared
print(deep[0])     # [1] — fully independent
Output
Method Copies Nested mutable objects
copy.copy Top level Shared
copy.deepcopy Recursive Independent

Use deep copy when handing off nested structures you must not alias (config snapshots, test fixtures).

A strong answer is:

"Shallow copy shares nested mutable objects; I use deepcopy when I need a fully independent nested structure like config snapshots or test fixtures."


Functions, decorators, and closures

Explain *args and **kwargs.

What interviewers are testing: Whether you can explain how *args and **kwargs forward positional and keyword arguments in wrappers and decorators.

  • *args — tuple of extra positional arguments
  • **kwargs — dict of extra keyword arguments
python
def demo(a, *args, b=10, **kwargs):
    return a, args, b, kwargs

demo(1, 2, 3, b=20, c=30)
# (1, (2, 3), 20, {'c': 30})
Output

Common uses:

  • Wrapper functions that forward to inner APIs
  • Decorators that accept arbitrary signatures
  • Framework hooks (Django views, pytest fixtures)

Full examples: python kwargs and args.

A strong answer is:

"*args collects extra positional arguments into a tuple and **kwargs collects keyword arguments into a dict—I use them in wrappers and decorators that forward to inner APIs."

What is a decorator and when would you write one?

What interviewers are testing: Whether you understand how decorators wrap callables and when cross-cutting behavior such as logging, caching, or authorization belongs in one.

A decorator wraps a function to add cross-cutting behavior without duplicating code:

  • Logging, timing, auth checks
  • Retry with backoff on flaky HTTP calls
  • Caching (functools.lru_cache)

Mechanically: @decorator applies func = decorator(func).

A strong answer is:

"Decorators wrap functions or classes to add reusable behavior such as logging, authorization, or caching. When I return a wrapper function, I normally use functools.wraps so metadata such as the function name and docstring is preserved."

How would you write a retry decorator with exponential backoff?

What interviewers are testing: Whether you can implement retry with selected exceptions, exponential backoff, jitter, and no sleep after the final failure.

Interviewers test closures, exceptions, and real API resilience:

python
import functools
import random
import time


def retry(times=3, base_delay=0.1, exceptions=(TimeoutError,)):
    if times < 1:
        raise ValueError("times must be at least 1")

    def decorator(fn):
        @functools.wraps(fn)
        def wrapper(*args, **kwargs):
            for attempt in range(times):
                try:
                    return fn(*args, **kwargs)
                except exceptions:
                    if attempt == times - 1:
                        raise

                    delay = base_delay * (2 ** attempt)
                    time.sleep(delay + random.uniform(0, delay * 0.1))

        return wrapper

    return decorator
Output

Senior follow-ups:

  • Retry only selected transient exceptions (requests.Timeout, TimeoutError)
  • Add jitter to avoid thundering herd
  • Log attempt count and final failure

A strong answer is:

"I wrap the call in a loop with exponential backoff and jitter, retry only specific exceptions like Timeout, and use functools.wraps so logs still show the original function name."

Lambda vs def — when to use each?

What interviewers are testing: Whether you reserve lambda for short expression-level callbacks and def for named multi-statement functions.

lambda def
Name Anonymous Named, reusable
Body Single expression Statements, docstring
Use Short key=/map callbacks Everything else
python
sorted(users, key=lambda u: u["last_login"])
Output

Prefer def for anything non-trivial—lambda bodies are limited to a single expression and cannot contain normal statements such as assignment statements, try, or while.

See python lambda function for more examples.

A strong answer is:

"I use lambda for short expression-level callbacks such as a sorting key; I use def when the logic deserves a name, multiple statements, documentation, or easier debugging."

What is a closure?

What interviewers are testing: Whether you explain closures as functions that capture enclosing scope and can fix the classic loop-lambda binding bug.

A closure is a function that remembers variables from its enclosing scope after the outer function returns.

Decorators rely on closures to hold configuration (times=3 in the retry decorator).

Interviewers may ask you to fix a classic loop bug:

python
# Bug: all lambdas see final i
funcs = [lambda: i for i in range(3)]

# Fix: default arg binds i at definition time
funcs = [lambda i=i: i for i in range(3)]
Output

A strong answer is:

"A closure captures variables from its enclosing scope, and I fix the classic loop-lambda bug by binding i with a default argument."


Object-oriented Python

Difference between @staticmethod, @classmethod, and instance methods?

What interviewers are testing: Whether you distinguish instance methods, classmethods, and staticmethods by what they receive and when each is appropriate.

Kind First arg Typical use
Instance method self Uses instance state
@classmethod cls Alternative constructors, factory methods
@staticmethod None Utility grouped with class namespace
python
class User:
    def __init__(self, name):
        self.name = name

    @classmethod
    def from_dict(cls, data):
        return cls(data["name"])

    @staticmethod
    def validate_name(name):
        return bool(name and name.strip())
Output

Use classmethod when you need polymorphic construction; staticmethod when logic belongs near the class but needs no self or cls.

A strong answer is:

"Instance methods get self, classmethods get cls for alternative constructors, and staticmethods are namespaced utilities that need neither."

How does inheritance and MRO work in Python?

What interviewers are testing: Whether you explain multiple inheritance, C3 MRO, and cooperative use of super().

Python supports multiple inheritance. Method Resolution Order (MRO) is the linearization C3 algorithm uses to pick which parent method runs.

python
class A:
    def ping(self):
        return "A"

class B(A):
    def ping(self):
        return "B"

class C(A):
    def ping(self):
        return "C"

class D(B, C):
    pass

print(D().ping())   # B
print(D.mro())    # D, B, C, A, object
Output

Interviewers want super() used correctly in cooperative multiple inheritance—not only single-parent examples.

A strong answer is:

"Python uses C3 MRO for multiple inheritance—I use super() cooperatively and prefer mixins over deep class hierarchies."

What are dataclasses and when do you prefer them?

What interviewers are testing: Whether you know when dataclasses reduce boilerplate and how frozen/hashable behavior depends on field options.

@dataclass generates __init__, __repr__, and comparison methods from typed fields—less boilerplate than manual classes.

python
from dataclasses import dataclass

@dataclass(frozen=True)
class Point:
    x: int
    y: int
Output
Prefer dataclass Prefer plain class / Pydantic
Internal DTOs, config records Rich validation at API boundary
frozen=True when you want immutable-style value objects; hashability also depends on dataclass options and field values FastAPI request models (Pydantic v2)

Senior note: dataclasses are not a replacement for domain logic—keep behavior methods purposeful.

A strong answer is:

"I use dataclasses for typed data containers with less boilerplate, and Pydantic or plain classes when I need rich validation or domain behavior."

Abstract base classes vs duck typing?

What interviewers are testing: Whether you choose ABCs for explicit contracts and duck typing for flexible protocols at runtime.

Duck typing: "If it quacks like a duck, use it"—no inheritance required; rely on protocols (file.read(), iterable behavior).

ABC (abc module): enforce interface contracts for frameworks and plugins.

python
from abc import ABC, abstractmethod

class Repository(ABC):
    @abstractmethod
    def get(self, id: int): ...
Output

Use ABCs when teams need explicit contracts; use duck typing for flexible utilities and tests (fakes without subclassing).

A strong answer is:

"I rely on duck typing for flexible utilities and ABCs when frameworks need an explicit interface contract that plugins must implement."

When would you use __slots__?

What interviewers are testing: Whether you know when slots trades fixed attribute layout and memory for flexibility—not micro-optimization folklore.

__slots__ restricts instance attributes to a fixed set. It can substantially reduce per-instance memory when creating many simple objects. Treat any attribute-access speed difference as secondary; use it when the fixed attribute model and inheritance trade-offs fit.

Trade-offs:

Benefit Cost
Lower per-instance memory for many objects Cannot freely add undeclared instance attributes
Explicit fixed attribute layout Inheritance and some tooling patterns become more complex

Use for high-volume value objects (event streams, parsers)—not default for every model (Django ORM models use their own machinery).

A strong answer is:

"I use slots for high-volume simple objects to cut memory when I accept the fixed attribute set and inheritance trade-offs."


Generators, iterators, and memory

What is a generator and why use one over a list?

What interviewers are testing: Whether you choose generators over lists for lazy iteration, constant memory, and pipeline-friendly processing.

A generator produces items lazily via yield (or a generator expression). A list materializes all items at once.

python
def read_lines(path):
    with open(path, encoding="utf-8") as f:
        for line in f:
            yield line.strip()
Output

Benefits:

  • Constant memory on large files
  • Pipeline-friendly (parse → filter → aggregate)
  • Can represent infinite sequences

Interview answer for "process 50GB CSV": generator or chunked pandas.read_csv(chunksize=...), never readlines() into RAM.

A strong answer is:

"Generators yield items lazily with constant memory; I choose them over lists for large files, pipelines, or infinite sequences."

Iterable vs iterator?

What interviewers are testing: Whether you distinguish an iterable's __iter__ from an iterator's stateful __next__ exhaustion.

  • Iterable — has __iter__() returning an iterator (list, dict, generator function result)
  • Iterator — has __iter__() and __next__(), raises StopIteration when exhausted
python
it = iter([1, 2, 3])
next(it)  # 1
Output

Generators are iterators. You can only consume a one-shot iterator once unless you recreate it.

A strong answer is:

"An iterable implements iter to produce an iterator; an iterator implements next and is exhausted after one pass unless recreated."

What does yield from do?

What interviewers are testing: Whether you explain yield from as delegation to another iterable or generator.

yield from delegates to another iterable/generator—flattens nested iteration and forwards send()/throw() in advanced coroutine code.

python
def chain(*iterables):
    for it in iterables:
        yield from it

list(chain([1, 2], [3]))  # [1, 2, 3]
Output

Practical use: composing pipelines without nested loops; async has async for analog patterns.

A strong answer is:

"yield from delegates iteration to another generator or iterable, flattening nested loops and forwarding send/throw in advanced coroutine code."


GIL, threading, multiprocessing, and asyncio

What is the GIL and why does it matter?

What interviewers are testing: Whether you explain GIL limits on standard CPython, free-threaded build trade-offs, and when processes or native code help.

CPython's standard build still uses a GIL that prevents multiple threads from executing Python bytecode simultaneously in one interpreter. A free-threaded build first became available experimentally in Python 3.13 and reached officially supported status in Python 3.14, where the GIL can be disabled for CPU parallelism across Python threads. Some extension modules that are not free-threading compatible may cause CPython to re-enable the GIL.

Implications:

Workload Typical approach
CPU-bound (math, encoding) multiprocessing, C extensions, or offload to Rust/Go worker
I/O-bound (HTTP, DB, disk) threading or asyncio while waiting on I/O
Mixed Process pool for CPU + async for I/O

A strong answer is:

"On standard CPython, the GIL prevents multiple threads in one interpreter from simultaneously executing Python bytecode, so threads are usually best for blocking I/O while processes are a common choice for CPU-heavy Python. Free-threaded builds change that trade-off, so I also consider interpreter build and extension compatibility."

See python multithreading and python multiprocessing.

Threading vs multiprocessing vs asyncio — how do you choose?

What interviewers are testing: Whether you match threading, multiprocessing, or asyncio to CPU-bound versus I/O-bound work and GIL constraints.

Model CPU parallelism Best for Caveat
threading Normally limited by the GIL on standard CPython; free-threaded Python can execute Python threads in parallel Blocking I/O libraries Watch shared mutable state; thread safety matters more in free-threaded builds
multiprocessing Yes — separate processes/interpreters CPU-heavy transforms Higher memory, serialization cost
asyncio Concurrency on an event loop, not CPU parallelism by itself Many concurrent I/O connections Requires async-compatible libraries

multiprocessing remains the portable default answer for CPU-heavy work on standard CPython, but threading = never CPU parallel is no longer universally correct in Python 3.14 free-threaded builds.

Example asyncio skeleton:

python
import asyncio

async def fetch(url):
    await asyncio.sleep(0.01)  # stand-in for I/O
    return url

async def main():
    results = await asyncio.gather(*(fetch(u) for u in ["a", "b", "c"]))
    print(results)

asyncio.run(main())
Output

Senior follow-up: uvicorn + FastAPI runs an ASGI event loop. In an async web handler, avoid blocking synchronous libraries on the event-loop thread. Use the framework or library's async API when available; otherwise isolate blocking work with mechanisms such as asyncio.to_thread() or framework-provided sync/async adapters.

A strong answer is:

"I use multiprocessing for CPU-bound work, threading or asyncio for I/O-bound concurrency, and never assume asyncio gives multi-core CPU parallelism."

When would you build a Python service with asyncio?

What interviewers are testing: Whether you choose asyncio for high fan-out I/O with async-native libraries rather than as a CPU parallelism fix.

Choose asyncio when:

  • High fan-out I/O (many HTTP/DB/WebSocket clients)
  • Latency-sensitive API aggregating multiple backends
  • Stack is async-native (FastAPI, httpx, asyncpg)

Avoid assuming asyncio fixes CPU bottlenecks—profile first.

Architecture sketch for notification fan-out:

  1. FastAPI accepts request
  2. asyncio.gather calls downstream services
  3. Push to queue (Redis/RabbitMQ) for slow work
  4. Worker process (Celery/sync) for CPU-heavy tasks

Compare with Node.js event loop interviews—similar I/O concurrency story, different runtime.

A strong answer is:

"I build with asyncio when I have high fan-out I/O and async-native libraries; CPU-heavy work still goes to processes or worker queues."

Common asyncio mistakes in interviews?

What interviewers are testing: Whether you recognize operations that block the event loop and know when to use async libraries, threads, or processes instead.

Mistake Fix
Calling time.sleep() in async route await asyncio.sleep() or run blocking code in executor
Blocking DB driver in async handler Async driver or asyncio.to_thread()
Fire-and-forget tasks without tracking Prefer asyncio.TaskGroup for related work; otherwise retain task refs and handle cancellation/exceptions
Shared mutable global state Pass dependencies; use connection pools
"Async everywhere" for CPU work Process pool or sync workers

Interviewers reject candidates who say asyncio gives multi-core parallelism for Python CPU work without mentioning processes.

A strong answer is:

"The common traps are blocking the event loop with sync sleep or DB drivers, fire-and-forget tasks without error handling, and expecting asyncio to parallelize CPU work."

How do you avoid blocking the event loop?

What interviewers are testing: Whether you avoid blocking the event loop with async libraries, asyncio.to_thread(), or worker offload.

  1. Use async libraries (httpx.AsyncClient, asyncpg)
  2. Wrap unavoidable sync calls: await asyncio.to_thread(blocking_fn, arg)
  3. Offload heavy CPU to Celery/RQ workers or multiprocessing pool
  4. Set timeouts on external calls
  5. Monitor event loop lag in production (metrics, slow request logs)

Django note: traditional sync views under WSGI are fine for many apps; ASGI + async views require the same discipline.

A strong answer is:

"I use async libraries first, wrap unavoidable blocking calls with asyncio.to_thread, and offload heavy CPU to worker processes or Celery."


Error handling, context managers, and testing

Explain try / except / else / finally.

What interviewers are testing: Whether you use specific exception handling, else/finally correctly, and preserve traceback context on re-raise.

python
try:
    result = risky()
except ValueError as exc:
    handle(exc)
else:
    # runs only if no exception
    log_success(result)
finally:
    # always runs — cleanup
    cleanup()
Output

Best practices interviewers expect:

  • Catch specific exceptions, not bare except:
  • Re-raise with raise or raise NewError(...) from exc
  • Use else to keep success path readable

More patterns: python try except.

A strong answer is:

"I catch specific exceptions, use else for the success path, always clean up in finally, and re-raise with raise ... from exc to preserve context."

What is a context manager and why use with?

What interviewers are testing: Whether you use context managers to guarantee setup and teardown for files, locks, and transactions.

Context managers guarantee setup/teardown (files, locks, DB transactions) via __enter__/__exit__ or @contextmanager.

python
from contextlib import contextmanager

@contextmanager
def tag(name):
    print(f"[{name}] start")
    try:
        yield name
    finally:
        print(f"[{name}] end")

with tag("job") as label:
    process(label)
Output

Prefer with open(...) over manual close() in finally—fewer leaked handles under exceptions.

A strong answer is:

"Context managers guarantee setup and teardown through with blocks—I use them for files, locks, and transactions instead of manual close in finally."

What pytest features do interviewers expect?

What interviewers are testing: Whether you know core pytest patterns such as fixtures, parametrize, and mocking external dependencies.

Feature Use
assert Simple tests without boilerplate
fixtures Shared DB, client, sample data
@pytest.mark.parametrize Table-driven edge cases
monkeypatch / unittest.mock Isolate external APIs
tmp_path File system tests

Example parametrized test:

python
import pytest

@pytest.mark.parametrize("text,expected", [
    ("", False),
    ("a", True),
])
def test_validate_name(text, expected):
    assert User.validate_name(text) == expected
Output

Senior bar: tests that catch regressions (N+1 query count, auth failures), not only happy path.

A strong answer is:

"I write plain assert tests, share setup with fixtures, cover edge cases with parametrize, and mock external APIs so CI stays fast and deterministic."

How do you mock external APIs in Python tests?

What interviewers are testing: Whether you patch at the use site, use AsyncMock or fakes for async dependencies, and keep unit tests off real external APIs.

  • Patch at use site (@patch("myapp.client.requests.get"))
  • Return Response fakes with controlled status/body
  • Assert important calls and payloads when interaction itself is part of the behavior
  • For async code, run coroutine tests with pytest-asyncio (or the project's async-test setup) and use AsyncMock or a controlled test transport/fake for asynchronous dependencies

Avoid hitting real payment or email APIs in unit tests—use interfaces + fakes so CI stays fast and deterministic.

A strong answer is:

"I patch at the use site, return controlled fakes, assert important calls when interaction matters, and use pytest-asyncio with AsyncMock for async dependencies—not real external APIs in unit tests."


Django, FastAPI, and Flask

Walk through the Django request lifecycle.

What interviewers are testing: Whether you can narrate Django's middleware-to-view request path and mention auth, CSRF, and WSGI/ASGI entry points.

High-level path (WSGI/ASGI):

  1. Server receives HTTP request
  2. Django middleware chain (security, sessions, auth, CSRF)
  3. URL resolver → view
  4. View uses ORM/forms/serializers
  5. Middleware processes response
  6. Connection cleanup / logging

Senior candidates mention CSRF on cookie-session apps, authentication middleware, and database connection handling—not only "URL → view → template."

Follow-up: WSGI sync vs ASGI async entry; choose ASGI when you need async views or WebSockets.

A strong answer is:

"A request passes through middleware for security and auth, hits a URL-resolved view that uses the ORM, then returns through middleware with CSRF and session handling."

What is the N+1 query problem and how do you fix it in Django?

What interviewers are testing: Whether you can identify N+1 queries and choose select_related() versus prefetch_related() based on the relationship.

N+1: one query for parent rows + one per row for related data.

python
# Bad: hits DB per book.author
for book in Book.objects.all():
    print(book.author.name)

# Better: select_related for ForeignKey
for book in Book.objects.select_related("author"):
    print(book.author.name)
Output
Tool Join type
select_related SQL JOIN — FK / OneToOne
prefetch_related Separate query + Python join — reverse FK, M2M

Senior nuance: prefetch_related on huge related sets can over-fetch—sometimes a targeted Prefetch queryset or raw SQL wins. Profile with django-debug-toolbar or query logging.

A strong answer is:

"N+1 is one query per related row—I fix it with select_related for ForeignKey joins and prefetch_related for reverse FK or many-to-many."

Django vs FastAPI — when would you use each?

What interviewers are testing: Whether you can justify Django versus FastAPI based on application shape, ecosystem needs, and async/API requirements.

Factor Django FastAPI
Admin/auth/ORM ecosystem Strong built-ins Usually compose separate libraries
Typical fit Full applications, admin-heavy systems Typed API services, async-heavy endpoints
Async Supports ASGI and async views/ORM operations, with sync boundaries still relevant Async-native route model
API schema Commonly through DRF/other tooling Automatic OpenAPI generation from routes/models

Hybrid pattern: Django for admin/auth + FastAPI for latency-sensitive read APIs—justify operational cost (two deployables, shared auth).

A strong answer is:

"I choose Django when its ORM, admin, authentication ecosystem, and full-application conventions save significant work. I choose FastAPI when the primary product is a typed API and async I/O or automatic OpenAPI generation is a strong fit."

Why does FastAPI use Pydantic models?

What interviewers are testing: Whether you understand how Pydantic validates data and how FastAPI turns validation failures into HTTP responses.

Pydantic provides:

  • Runtime validation of request/response bodies
  • Structured validation errors at parse time; FastAPI maps request-validation failures to HTTP responses such as 422
  • Type hints → OpenAPI / Swagger docs
  • Pydantic v2 Rust core → faster validation

Senior angle: schema is the contract between frontend and backend—reduces "works on my machine JSON" bugs until integration tests exist.

python
from pydantic import BaseModel, Field

class CreateUser(BaseModel):
    email: str = Field(min_length=3)
    age: int = Field(ge=18)
Output

A strong answer is:

"FastAPI integrates Pydantic models to parse and validate request/response data and derive API schemas from Python types. FastAPI then turns request-validation failures into structured HTTP errors."

How do you handle auth in Flask when the framework is minimal?

What interviewers are testing: Whether you can choose Flask auth patterns based on SPA versus server-rendered clients and threat model.

Flask does not ship auth—you choose:

Approach Fit
Flask-Login Session/cookie web apps
JWT middleware SPA + API token
OAuth lib Social / enterprise SSO

Senior response: ask clarifying questions (SPA vs server-rendered? refresh tokens?) before picking.

For DB patterns with Flask, see Flask SQLAlchemy.

A strong answer is:

"Flask has no built-in auth—I pick Flask-Login for cookie sessions, JWT middleware for SPAs, or OAuth for SSO based on the client and threat model."


Full stack Python, SQL, and production

What steps matter when building a production Python REST API?

What interviewers are testing: Whether you name production API concerns beyond route handlers—validation, auth, logging, health checks, migrations, and CI.

  1. Validation at boundary (Pydantic, DRF serializers)
  2. AuthN/AuthZ — JWT or session; role checks on sensitive routes
  3. Structured logging + request IDs
  4. Health/readiness endpoints
  5. Migrations (Django/Alembic) with safe rollout plan
  6. Tests — unit + API integration
  7. Container image (slim base, non-root user)
  8. CI — lint, test, type check (ruff, mypy optional)

Interviewers want operational awareness—not only @app.get.

Packaging reference: create a Python package.

A strong answer is:

"Production APIs need boundary validation, auth, structured logging, health checks, migrations, tests, non-root containers, and CI gates—not just route handlers."

How does a full stack Python developer connect React to Django or FastAPI?

What interviewers are testing: Whether you can explain React-to-Python API integration with CORS, auth, and validation boundaries.

Typical flow:

  1. React form → fetch('/api/items', { method: 'POST', body: JSON })
  2. FastAPI/Django validates body, persists via ORM
  3. JSON response → React updates state (React Query/SWR)
  4. CORS configured for dev origin; cookies need credentials + CSRF strategy
Auth style Browser note
JWT in memory/header CORS + XSS discipline
HttpOnly cookie CSRF protection on mutating routes

Cross-read full stack developer interview questions for layer-integration scenarios beyond Python-only depth.

A strong answer is:

"React calls a JSON API with fetch or React Query, the Python backend validates with Pydantic or DRF, and I configure CORS and CSRF correctly for cookie or JWT auth."

How much SQL do Python interviews expect?

What interviewers are testing: Whether you can use ORMs productively while still reading and writing SQL for performance and reporting.

Backend Python roles expect you to read and write SQL, not only ORM magic:

  • JOINs, GROUP BY, indexes
  • Explain an EXPLAIN plan at high level
  • Know when ORM generates inefficient SQL

Practice with SQL technical interview questions.

Strong answer: "I use ORM for productivity but drop to SQL for reporting queries and performance fixes."

A strong answer is:

"I use the ORM for productivity but write SQL for reporting and performance fixes—I can read JOINs, indexes, and EXPLAIN plans when queries regress."

What deployment and CI/CD topics appear for Python developers?

What interviewers are testing: Whether you know common Python deployment topics such as containers, workers, migrations, background jobs, and observability.

Common topics:

  • Docker multi-stage builds, uv/pip layer caching — see Docker interview questions for container-focused follow-ups
  • Gunicorn/uvicorn workers vs async workers
  • Environment config — 12-factor, secrets not in git
  • Migrations on deploy — expand/contract pattern
  • Celery/RQ for background jobs
  • Observability — Sentry, Prometheus, structured logs

Senior: blue/green or rolling deploys, feature flags, rollback when migration fails.

A strong answer is:

"I ship Python services in slim Docker images with Gunicorn or uvicorn, run migrations safely on deploy, and use CI for lint, test, and observability hooks."


Live coding, system design, and behavioral

Live coding: flatten a nested dictionary with dot-separated keys.

What interviewers are testing: Whether you solve recursive dictionary flattening with clear edge-case questions and readable communication.

Common prompt testing recursion and edge cases:

python
def flatten(d, parent=""):
    out = {}
    for key, value in d.items():
        full_key = f"{parent}.{key}" if parent else key
        if isinstance(value, dict):
            out.update(flatten(value, full_key))
        else:
            out[full_key] = value
    return out

print(flatten({"a": 1, "b": {"c": 2, "d": {"e": 3}}}))
# {'a': 1, 'b.c': 2, 'b.d.e': 3}
Output

Clarify with interviewer:

  • Are values only dicts and scalars?
  • What about lists of dicts?
  • Mutate or return new dict?

Think aloud: base case (scalar), recursive case (dict), string key building.

A strong answer is:

"I recurse into nested dicts, build dot-separated keys, handle the scalar base case, and clarify edge cases like lists before coding."

System design: real-time notification service in Python — what constraints do you mention?

What interviewers are testing: Whether you design a Python notification service with realistic async, queue, worker, and GIL-aware constraints.

Cover:

  1. Ingress — HTTP or WebSocket gateway (FastAPI/ASGI)
  2. Fan-out — asyncio for I/O; processes if CPU-heavy formatting
  3. Queue — Redis/RabbitMQ for durability
  4. Workers — Celery consumers for email/push providers
  5. Storage — Postgres for preferences, idempotency keys
  6. Scale — horizontal API replicas; sticky sessions only if needed for WS

Python-specific: acknowledge GIL vs free-threaded Python—do not promise single-process multi-core CPU on standard CPython; partition CPU work across processes or specialized services when needed.

Compare architecture patterns in full stack developer interview questions.

A strong answer is:

"I separate an async API gateway from a durable queue and worker processes, store preferences in Postgres, and never promise single-process multi-core CPU on standard CPython."

What changed with multiprocessing defaults in Python 3.14?

What interviewers are testing: Whether your multiprocessing knowledge reflects Python 3.14's changed POSIX defaults rather than assuming Linux always uses fork.

For Python 3.14, fork is no longer the default on any platform. On POSIX systems that support it—including normal Linux—the multiprocessing default changed to forkserver. macOS and Windows use spawn.

Platform Python 3.14 default
Linux / supported POSIX forkserver
macOS spawn
Windows spawn

fork is still available on supported POSIX systems, but code that requires it must request it explicitly.

Why it matters in interviews:

  • fork in a multithreaded parent can duplicate locks and interpreter state unsafely
  • libraries that assumed inherited memory from fork may break
  • explicit multiprocessing.set_start_method() or executor configuration is safer in production services

A strong answer is:

"In Python 3.14, Linux/POSIX multiprocessing normally defaults to forkserver, not fork; macOS and Windows use spawn. If my code relies on fork, I request that context explicitly and verify it is safe for the application."

What changed with free-threaded Python?

What interviewers are testing: Whether you explain free-threaded Python's supported status, GIL trade-offs, and extension compatibility caveats.

Free-threaded Python (officially supported in Python 3.14) lets you build CPython without the traditional GIL so multiple Python threads can execute bytecode in parallel within one process.

Build Behavior
Traditional/default CPython GIL limits parallel Python bytecode execution
Free-threaded CPython GIL disabled; CPU parallelism across Python threads possible

Interview points:

  • Thread safety and shared mutable state matter more
  • C extension compatibility and performance can differ from GIL builds; a third-party extension that is not ready for free threading may cause CPython to re-enable the GIL
  • multiprocessing remains a safe portable choice for CPU work, but threading is no longer automatically ruled out for CPU parallelism on free-threaded builds

A strong answer is:

“Standard CPython still uses the GIL, but Python 3.14 adds supported free-threaded builds. I choose threading, multiprocessing, or asyncio based on workload, library support, and whether the deployment uses a GIL or free-threaded interpreter.”

What are multiple interpreters / InterpreterPoolExecutor-style isolation?

What interviewers are testing: Whether you distinguish interpreter isolation from concurrency and explain when InterpreterPoolExecutor enables multi-core parallelism.

Python 3.14 added the high-level concurrent.interpreters API for managing isolated interpreters in one process. An interpreter by itself is an isolated execution context, not a concurrency mechanism—concurrency requires threads or another execution mechanism.

InterpreterPoolExecutor combines worker threads with separate interpreters. Because each interpreter has its own GIL, workers can execute Python code in parallel on multiple cores.

Third-party and extension compatibility is still a consideration; not every package supports multiple interpreters correctly.

A strong answer is:

"Multiple interpreters give isolated Python runtime state within one process. The interpreters API itself doesn't create concurrency, but InterpreterPoolExecutor combines separate interpreters with worker threads and can achieve multi-core Python parallelism because each interpreter has its own GIL."

Protocols vs ABCs — how do they differ?

What interviewers are testing: Whether you contrast structural Protocol typing with nominal ABC inheritance and know runtime-check limits.

Modern static typing adds typing.Protocol for structural typing: a class can satisfy the contract without inheriting from the protocol. Normal protocols are primarily a static typing mechanism; @runtime_checkable can enable limited isinstance()/issubclass() checks, but runtime protocol checks verify attribute presence rather than full type signatures.

Approach Contract style
Duck typing If it behaves like the interface, use it
ABC Explicit inheritance/registration
Protocol Structural typing — methods/attributes define the contract
python
from typing import Protocol

class Writer(Protocol):
    def write(self, data: str) -> int: ...

def save(target: Writer, text: str) -> None:
    target.write(text)
Output

A strong answer is:

“ABCs enforce nominal contracts through inheritance. Protocols are primarily for static structural typing; @runtime_checkable supports limited runtime checks when needed.”

Final-week checklist and one behavioral tip?

What interviewers are testing: Whether you have a rehearsed final-week checklist linking core Python traps, frameworks, live coding, and behavioral stories.

Technical drills:

  • Explain mutable default, GIL, and asyncio vs threading without notes
  • Write retry decorator and flatten dict on whiteboard
  • Walk through Django N+1 fix with select_related / prefetch_related
  • Defend Django vs FastAPI for a JD-specific scenario
  • One pytest fixture + parametrized test from memory
  • Sketch FastAPI + worker queue deploy diagram

Cross-prep:

  • SQL interviews for JOIN/window practice
  • Data science interviews if role blends ML
  • Git interviews for PR workflow stories

Behavioral (STAR): Prepare one story where you fixed a production Python incident—bad deploy, runaway query, memory leak, or async blocking—with metrics (latency, error rate, query count).

A strong close:

I rehearse framework depth for the JD, core Python traps aloud, and three STAR stories tied to services I actually shipped—not slides of syntax trivia.

A strong answer is:

"I rehearse mutability, GIL, asyncio, N+1 fixes, and one live-coding problem aloud, and I close behavioral answers with measurable impact and my specific role."


Pattern cheat sheet (quick reference)

Topic Remember
Mutable default Use None sentinel
Large data Generators, chunked reads
CPU parallel multiprocessing / native code
I/O concurrent asyncio or threads
Django N+1 select_related, prefetch_related
API contracts Pydantic / DRF serializers
Tests pytest fixtures + parametrize
Deploy Container + migrations + health check

References


Summary

Python developer interviews connect language fundamentals to framework and production choices—mutable defaults, generator memory, GIL-aware concurrency, ORM query counts, and async discipline on FastAPI services. Answer aloud and compare your structure to each section. Pair with SQL and full stack prep when the role spans the whole stack.

Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)