Python Engineer Interview Preparation 2026: Concepts & Design Questions
I've used Python for 10 years—from quick scripts to distributed systems. What I've learned: knowing list comprehensions isn't enough. Here are the questions that reveal whether you truly understand Python.
Domain Overview
Python has evolved from a scripting language to powering everything from web backends to machine learning pipelines to infrastructure automation. In 2026, Python developers are expected to understand not just the syntax, but the ecosystem—web frameworks, async programming, data processing, and increasingly, AI/ML integration.
What makes Python tricky in interviews is that it's deceptively simple. The basics are easy to learn, which means interviewers dig deeper. They want to see if you understand Python's memory model, the GIL, generator expressions, context managers, and when to use different data structures.
The best Python developers I know write code that's not just functional but Pythonic—readable, maintainable, and leveraging the language's strengths rather than fighting against them.
Key Skills Interviewers Look For
- Core Python: Data structures, OOP, decorators, generators, context managers
- Web Frameworks: Django or Flask/FastAPI, REST APIs, ORM patterns
- Async Programming: asyncio, concurrent.futures, when to use threads vs processes
- Testing: pytest, mocking, test-driven development
- Data Processing: Pandas, NumPy basics, handling large datasets
- Package Management: pip, virtualenv, poetry, dependency management
- Performance: Profiling, optimization, understanding the GIL
- Best Practices: PEP 8, type hints, clean code principles
Fundamental Questions (Q1-Q15)
1. What's the difference between a list and a tuple? When would you use each?
Expert Answer:
Lists are mutable (can be modified), tuples are immutable (cannot be changed after creation).
{`# List - mutable
fruits = ["apple", "banana"]
fruits.append("orange") # Works
# Tuple - immutable
coords = (10, 20)
coords[0] = 15 # TypeError!`}
When to use tuples:
- Fixed data that shouldn't change (coordinates, RGB values)
- Dictionary keys (lists can't be keys because they're mutable)
- Return multiple values from functions
- Performance-critical code (tuples are slightly faster and use less memory)
When to use lists:
- Collections that need to grow/shrink
- When you need to modify elements
- Homogeneous data (list of users, list of orders)
2. Explain Python's GIL (Global Interpreter Lock). Why does it matter?
Expert Answer:
The GIL is a mutex that allows only one thread to execute Python bytecode at a time, even on multi-core machines.
Why it exists: CPython's memory management (reference counting) isn't thread-safe. The GIL prevents race conditions on reference counts.
Impact:
- CPU-bound tasks: Multiple threads won't speed things up (use multiprocessing instead)
- I/O-bound tasks: GIL is released during I/O, so threading still helps
{`# CPU-bound: Use multiprocessing
from multiprocessing import Pool
with Pool(4) as p:
results = p.map(cpu_heavy_function, data)
# I/O-bound: Threading or asyncio works fine
import asyncio
results = await asyncio.gather(*[fetch(url) for url in urls])`}
Note: Python 3.12+ has work toward a "free-threaded" mode (no GIL), but it's experimental as of 2026.
3. What are decorators? Write a simple one.
Expert Answer:
Decorators are functions that modify the behavior of other functions. They're syntactic sugar for wrapping functions.
{`import functools
import time
def timer(func):
@functools.wraps(func) # Preserves function metadata
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
elapsed = time.perf_counter() - start
print(f"{func.__name__} took {elapsed:.4f}s")
return result
return wrapper
@timer
def slow_function():
time.sleep(1)
return "done"
# Equivalent to: slow_function = timer(slow_function)`}
Common uses:
- @property, @staticmethod, @classmethod (built-in)
- Logging, timing, caching (@lru_cache)
- Authentication/authorization in web frameworks
- Retry logic, rate limiting
4. Explain the difference between `is` and `==`.
Expert Answer:
`==` checks if values are equal (calls __eq__)
`is` checks if two references point to the same object in memory
{`a = [1, 2, 3]
b = [1, 2, 3]
c = a
a == b # True (same values)
a is b # False (different objects)
a is c # True (same object)
# Gotcha: Python interns small integers
x = 256
y = 256
x is y # True (interned)
x = 257
y = 257
x is y # False (not interned)`}
Best practice: Use `is` only for comparing to `None`, `True`, `False`. Use `==` for value comparison.
5. What are generators? How do they differ from regular functions?
Expert Answer:
Generators are functions that use `yield` instead of `return`. They produce values lazily, one at a time, maintaining state between calls.
{`# Regular function: creates entire list in memory
def get_squares_list(n):
return [x**2 for x in range(n)] # Uses O(n) memory
# Generator: produces values on demand
def get_squares_gen(n):
for x in range(n):
yield x**2 # Uses O(1) memory
# Generator expression (like list comprehension but lazy)
squares = (x**2 for x in range(1000000))
# Memory comparison for n=1,000,000:
# List: ~8MB | Generator: ~100 bytes`}
When to use generators:
- Processing large files line by line
- Streaming data that doesn't fit in memory
- Infinite sequences
- Pipeline processing (chain generators together)
6. Explain Python's method resolution order (MRO) in multiple inheritance.
Expert Answer:
MRO determines the order in which Python searches for methods in a class hierarchy. Python uses the C3 linearization algorithm.
{`class A:
def method(self):
print("A")
class B(A):
def method(self):
print("B")
class C(A):
def method(self):
print("C")
class D(B, C):
pass
D().method() # Prints "B"
print(D.__mro__)
# (, , , , )`}
Key points:
- Children come before parents
- Order of base classes is preserved
- super() follows MRO, not just immediate parent
7. What is a context manager? How do you create one?
Expert Answer:
Context managers handle setup and teardown automatically, ensuring resources are properly managed even if exceptions occur.
{`# Using a context manager
with open("file.txt") as f:
data = f.read()
# File is automatically closed, even if exception occurs
# Creating a context manager (class-based)
class DatabaseConnection:
def __enter__(self):
self.conn = connect_to_db()
return self.conn
def __exit__(self, exc_type, exc_val, exc_tb):
self.conn.close()
return False # Don't suppress exceptions
# Creating a context manager (decorator-based)
from contextlib import contextmanager
@contextmanager
def timer():
start = time.time()
yield # Code in 'with' block runs here
print(f"Elapsed: {time.time() - start}s")`}
8-15. More Fundamental Questions:
- 8. What are *args and **kwargs? Give examples of when to use them.
- 9. Explain the difference between shallow copy and deep copy.
- 10. What are dunder (magic) methods? Name five important ones.
- 11. How does Python's garbage collection work?
- 12. What's the difference between @staticmethod and @classmethod?
- 13. Explain list comprehensions vs map/filter. Which is more Pythonic?
- 14. What are Python's built-in data structures? Describe their time complexities.
- 15. How do you handle exceptions in Python? What's the difference between except and except Exception?
Intermediate Questions (Q16-Q35)
16. Explain async/await in Python. When should you use it?
Expert Answer:
async/await enables cooperative multitasking for I/O-bound operations. Unlike threads, coroutines yield control explicitly.
{`import asyncio
import aiohttp
async def fetch_url(session, url):
async with session.get(url) as response:
return await response.text()
async def main():
urls = ["http://example.com"] * 100
async with aiohttp.ClientSession() as session:
# Run 100 requests concurrently
tasks = [fetch_url(session, url) for url in urls]
results = await asyncio.gather(*tasks)
return results
# Synchronous: 100 requests × 0.1s = 10 seconds
# Async: All concurrent ≈ 0.1 seconds`}
Use async when:
- Many concurrent I/O operations (HTTP requests, database queries)
- WebSocket servers, real-time applications
- Building APIs with FastAPI or similar frameworks
Don't use async when:
- CPU-bound tasks (use multiprocessing)
- Simple scripts with few I/O operations
- When sync libraries don't have async alternatives
17. How would you design a REST API with Flask or FastAPI?
Expert Answer:
{`# FastAPI example (modern, async, auto-docs)
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import Optional
app = FastAPI()
class User(BaseModel):
name: str
email: str
age: Optional[int] = None
users_db = {}
@app.get("/users/{user_id}")
async def get_user(user_id: int):
if user_id not in users_db:
raise HTTPException(status_code=404, detail="User not found")
return users_db[user_id]
@app.post("/users", status_code=201)
async def create_user(user: User):
user_id = len(users_db) + 1
users_db[user_id] = user.dict()
return {"id": user_id, **user.dict()}
# Features: Type validation, auto OpenAPI docs, async support`}
Best practices:
- Use Pydantic for request/response validation
- Proper HTTP status codes (201 for create, 404 for not found)
- Dependency injection for database sessions
- Separate routes, models, services layers
18. Explain Python type hints. Why use them?
Expert Answer:
Type hints are optional annotations that document expected types. Python doesn't enforce them at runtime—they're for tooling and documentation.
{`from typing import List, Dict, Optional, Union, Callable
def process_users(
users: List[Dict[str, str]],
transform: Callable[[str], str],
limit: Optional[int] = None
) -> List[str]:
"""Process user names with optional limit."""
names = [transform(u["name"]) for u in users]
return names[:limit] if limit else names
# Python 3.10+ simplified syntax
def process(items: list[dict[str, str]]) -> list[str]:
...`}
Benefits:
- IDE autocompletion and error detection
- Self-documenting code
- Catch bugs before runtime with mypy
- Easier refactoring
19. How do you write unit tests in Python? Explain pytest features.
Expert Answer:
{`import pytest
from unittest.mock import Mock, patch
# Basic test
def test_addition():
assert 1 + 1 == 2
# Fixtures for setup/teardown
@pytest.fixture
def database():
db = create_test_db()
yield db
db.cleanup()
def test_user_creation(database):
user = database.create_user("test@example.com")
assert user.email == "test@example.com"
# Parametrized tests
@pytest.mark.parametrize("input,expected", [
("hello", "HELLO"),
("World", "WORLD"),
("", ""),
])
def test_uppercase(input, expected):
assert input.upper() == expected
# Mocking external services
@patch("myapp.services.send_email")
def test_registration(mock_send):
mock_send.return_value = True
result = register_user("test@example.com")
mock_send.assert_called_once()`}
20-35. More Intermediate Questions:
- 20. How does Django's ORM work? Explain QuerySets and lazy evaluation.
- 21. What is the difference between threads and processes in Python?
- 22. Explain Python's descriptor protocol (__get__, __set__, __delete__).
- 23. How do you handle configuration and environment variables?
- 24. What is metaclass? When would you use one?
- 25. Explain Python packaging: setup.py vs pyproject.toml.
- 26. How do you profile Python code? What tools do you use?
- 27. What are dataclasses? How do they compare to regular classes and namedtuples?
- 28. Explain the walrus operator (:=) and when to use it.
- 29. How does Python's import system work? Explain circular imports.
- 30. What is monkey patching? When is it appropriate?
- 31. Explain Django middleware and how to create custom middleware.
- 32. How do you handle database migrations in Django or Alembic?
- 33. What is Celery? How do you design background task queues?
- 34. Explain Python's logging module and best practices.
- 35. How do you secure a Python web application?
Advanced & Real-World Questions (Q36-Q50)
36. Design a rate limiter using Python. Handle distributed scenarios.
Expert Answer:
{`import redis
import time
from functools import wraps
class RateLimiter:
def __init__(self, redis_client, limit=100, window=60):
self.redis = redis_client
self.limit = limit
self.window = window
def is_allowed(self, key: str) -> tuple[bool, int]:
"""Sliding window rate limiter."""
now = time.time()
window_start = now - self.window
pipe = self.redis.pipeline()
pipe.zremrangebyscore(key, 0, window_start)
pipe.zadd(key, {str(now): now})
pipe.zcard(key)
pipe.expire(key, self.window)
_, _, count, _ = pipe.execute()
return count <= self.limit, self.limit - count
def rate_limit(limiter: RateLimiter, key_func):
def decorator(func):
@wraps(func)
async def wrapper(*args, **kwargs):
key = f"ratelimit:{key_func(*args, **kwargs)}"
allowed, remaining = limiter.is_allowed(key)
if not allowed:
raise RateLimitExceeded(remaining)
return await func(*args, **kwargs)
return wrapper
return decorator`}
37. How would you optimize a slow Python application?
Expert Answer:
1. Profile first—don't guess:
{`# cProfile for CPU profiling
python -m cProfile -s cumtime myapp.py
# line_profiler for line-by-line
@profile
def slow_function():
...
# memory_profiler for memory
@profile
def memory_heavy():
...`}
2. Common optimizations:
- Algorithmic: O(n²) → O(n log n), use correct data structures
- Caching: @lru_cache for expensive computations
- Database: N+1 queries, missing indexes, query optimization
- I/O: Async for concurrent I/O, connection pooling
- Memory: Generators instead of lists, __slots__ for classes
3. When Python isn't enough:
- NumPy/Pandas for numerical operations
- Cython for hot paths
- PyPy for long-running processes
38-50. More Advanced Questions:
- 38. Design a Python ETL pipeline that processes millions of records.
- 39. How would you implement a connection pool from scratch?
- 40. Explain how you'd debug a memory leak in a Python application.
- 41. Design a plugin system using Python's import machinery.
- 42. How do you handle secrets and sensitive configuration in Python apps?
- 43. Explain how to build a CLI tool with argparse or click.
- 44. How would you implement retry logic with exponential backoff?
- 45. Design a caching layer that handles cache invalidation properly.
- 46. How do you containerize a Python application with Docker?
- 47. Explain Python's asyncio internals (event loop, coroutines, tasks).
- 48. How would you implement a simple ORM from scratch?
- 49. Design an API client library with retry, caching, and rate limiting.
- 50. How do you ensure backward compatibility when evolving a Python library?
Ace Your Python Interview
Practice explaining Python concepts out loud with Craqly. Get real-time feedback on your technical explanations.
Common Mistakes Candidates Make
❌ What to Avoid:
- • Writing Java/C++ style Python (getters/setters everywhere)
- • Not knowing when to use list vs dict vs set
- • Ignoring Python's built-in functions and itertools
- • Using global variables for state management
- • Not handling exceptions properly
- • Writing code without type hints in modern Python
✓ What Works:
- • Write Pythonic code (use the language features)
- • Know the standard library well
- • Understand when to use different data structures
- • Use context managers for resource management
- • Write comprehensive tests
- • Follow PEP 8 and use type hints
Pro Tips from Interviewers
Know the Zen of Python
Run `import this` and understand its principles. "Simple is better than complex" should guide your code.
Explain trade-offs
"I'd use asyncio here because..." shows deeper understanding than just knowing the syntax.
Discuss real projects
Be ready to dive deep into Python projects you've built. Interviewers love hearing about challenges you solved.
Related Interview Guides
Comments
Leave a comment
No comments yet. Be the first to share your thoughts!
Related Articles
SRE Interview Help: Top Questions on Reliability Engineering
Real SRE interview questions covering SLOs, error budgets, incident management, capacity planning, and toil reduction — with answer guidance from engineers who have lived through production outages.
Read moreFull Stack Developer Interview Help: Frontend, Backend, and Everything Between
The full stack interview covers everything from React hooks to database indexing. Here are the questions that actually come up, with practical answer guidance for each.
Read moreQA Engineer Interview Help: Testing and Automation Questions
The most common QA engineer interview questions on manual testing, automation frameworks, API testing, and CI/CD — with practical answer guidance for each.
Read more