Senior Python Backend Developer Interview Guide

๐ Python Core Concepts
1. Object-Oriented Programming (OOP)
Classes and Inheritance
class Animal:
def __init__(self, name, species):
self.name = name
self.species = species
def __str__(self):
return f"{self.name} is a {self.species}"
def __repr__(self):
return f"Animal(name={self.name!r}, species={self.species!r})"
def speak(self):
pass
class Dog(Animal):
def __init__(self, name, breed):
super().__init__(name, "Dog")
self.breed = breed
def speak(self): # Polymorphism
return "Woof!"
Key Interview Points:
__init__: Constructor for initialization__str__: Human-readable string (for end users)__repr__: Unambiguous representation (for developers/debugging)Polymorphism: Same method name, different implementations
Inheritance: Code reuse through parent-child relationships
2. Decorators & Context Managers
Decorators
import time
from functools import wraps
def timing_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} took {end - start:.2f}s")
return result
return wrapper
@timing_decorator
def slow_function():
time.sleep(2)
return "Done"
Context Managers
# Using 'with' statement
with open('file.txt', 'r') as f:
data = f.read()
# File automatically closed
# Custom context manager
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 # Propagate exceptions
Why use them:
Decorators: Add functionality without modifying original code (logging, auth, caching)
Context Managers: Ensure proper resource management (files, connections, locks)
3. Iterators & Generators
Iterators
class Counter:
def __init__(self, max_val):
self.max_val = max_val
self.current = 0
def __iter__(self):
return self
def __next__(self):
if self.current >= self.max_val:
raise StopIteration
self.current += 1
return self.current
Generators (More Efficient)
def counter(max_val):
current = 0
while current < max_val:
current += 1
yield current # Returns value and pauses execution
# Memory efficient for large datasets
numbers = counter(1000000) # Doesn't create list in memory
Interview Insight: Generators use lazy evaluation, saving memory for large datasets. They're ideal for processing streams or large files.
4. Async/Await
import asyncio
import aiohttp
async def fetch_data(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
return await response.json()
async def main():
urls = ['http://api1.com', 'http://api2.com', 'http://api3.com']
tasks = [fetch_data(url) for url in urls]
results = await asyncio.gather(*tasks) # Concurrent execution
return results
# Run event loop
asyncio.run(main())
Key Concepts:
Event Loop: Manages async task execution
Non-blocking I/O: Program doesn't wait for I/O operations
Concurrency vs Parallelism: Async handles I/O-bound tasks efficiently
5. Global Interpreter Lock (GIL)
What is GIL?
Python's mutex preventing multiple threads from executing Python bytecode simultaneously
Impact: Multi-threading doesn't provide true parallelism for CPU-bound tasks
When it matters:
โ CPU-bound tasks: Use
multiprocessinginsteadโ I/O-bound tasks: Threading works fine (GIL released during I/O)
# For CPU-bound tasks
from multiprocessing import Pool
def cpu_intensive(x):
return sum(i*i for i in range(x))
with Pool(4) as p:
results = p.map(cpu_intensive, [10000000] * 4)
๐ Django Framework
1. MVC Architecture (MTV in Django)
Models (Data Layer)
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
author = models.ForeignKey('User', on_delete=models.CASCADE)
class Meta:
ordering = ['-created_at']
indexes = [models.Index(fields=['created_at'])]
Views (Logic Layer)
from django.shortcuts import render
from .models import Post
def post_list(request):
posts = Post.objects.select_related('author').all()
return render(request, 'posts/list.html', {'posts': posts})
Templates (Presentation Layer)
{% for post in posts %}
<h2>{{ post.title }}</h2>
<p>By {{ post.author.username }}</p>
{% endfor %}
2. ORM Query Optimization
select_related vs prefetch_related
# โ N+1 Query Problem
posts = Post.objects.all()
for post in posts:
print(post.author.username) # Hits DB each time!
# โ
select_related (for ForeignKey, OneToOne)
posts = Post.objects.select_related('author').all() # 1 query with JOIN
# โ
prefetch_related (for ManyToMany, reverse ForeignKey)
posts = Post.objects.prefetch_related('comments').all() # 2 queries total
Aggregation
from django.db.models import Count, Avg
Post.objects.aggregate(
total_posts=Count('id'),
avg_comments=Avg('comments__count')
)
3. Middleware
class TimingMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
start_time = time.time()
response = self.get_response(request) # View processing
duration = time.time() - start_time
response['X-Request-Duration'] = str(duration)
return response
Flow: process_request โ View โ process_response
4. Signals
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.core.mail import send_mail
@receiver(post_save, sender=User)
def send_welcome_email(sender, instance, created, **kwargs):
if created: # Only for new users
send_mail(
'Welcome!',
'Thanks for joining us.',
'from@example.com',
[instance.email]
)
Use Cases: Email notifications, logging, cache invalidation (decoupled from main logic)
5. Authentication & Permissions
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework_simplejwt.tokens import RefreshToken
@api_view(['POST'])
@permission_classes([IsAuthenticated])
def protected_view(request):
return Response({'message': f'Hello {request.user.username}'})
# JWT Token generation
def get_tokens_for_user(user):
refresh = RefreshToken.for_user(user)
return {
'refresh': str(refresh),
'access': str(refresh.access_token),
}
๐งช Flask Framework
1. Blueprints (Modular Routing)
# auth/routes.py
from flask import Blueprint
auth_bp = Blueprint('auth', __name__, url_prefix='/auth')
@auth_bp.route('/login', methods=['POST'])
def login():
return {'message': 'Login endpoint'}
# app.py
from flask import Flask
from auth.routes import auth_bp
app = Flask(__name__)
app.register_blueprint(auth_bp)
2. App Factory Pattern
def create_app(config_name='development'):
app = Flask(__name__)
app.config.from_object(f'config.{config_name}Config')
# Initialize extensions
db.init_app(app)
migrate.init_app(app, db)
jwt.init_app(app)
# Register blueprints
app.register_blueprint(auth_bp)
app.register_blueprint(api_bp)
return app
# Easy testing with different configs
app = create_app('testing')
3. Request Context
from flask import g, request, session
@app.before_request
def load_user():
g.user = get_current_user() # Available throughout request
@app.route('/dashboard')
def dashboard():
user_agent = request.headers.get('User-Agent')
user_id = session.get('user_id') # Encrypted cookie
current_user = g.user # From before_request
return render_template('dashboard.html')
โก FastAPI Framework
1. Why FastAPI is Fast
from fastapi import FastAPI, Depends
from pydantic import BaseModel
from typing import List
app = FastAPI()
class Item(BaseModel):
name: str
price: float
is_available: bool = True
@app.post("/items/", response_model=Item)
async def create_item(item: Item): # Auto validation
# Async = non-blocking I/O
await save_to_db(item)
return item
Speed factors:
Async: Handles concurrent requests efficiently
Pydantic: Fast data validation using type hints
Starlette: High-performance ASGI framework
2. Dependency Injection
from fastapi import Depends
from sqlalchemy.orm import Session
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@app.get("/users/{user_id}")
async def get_user(user_id: int, db: Session = Depends(get_db)):
user = db.query(User).filter(User.id == user_id).first()
return user
Benefits: Reusable dependencies for DB connections, auth, logging
3. JWT Authentication
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from jose import JWTError, jwt
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
async def get_current_user(token: str = Depends(oauth2_scheme)):
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise HTTPException(status_code=401)
except JWTError:
raise HTTPException(status_code=401)
return get_user_from_db(username)
@app.get("/protected")
async def protected_route(current_user: User = Depends(get_current_user)):
return {"user": current_user.username}
4. Background Tasks
from fastapi import BackgroundTasks
def send_email(email: str, message: str):
# Simulated email sending
time.sleep(5)
print(f"Email sent to {email}")
@app.post("/signup")
async def signup(email: str, background_tasks: BackgroundTasks):
# Response sent immediately
background_tasks.add_task(send_email, email, "Welcome!")
return {"message": "Signup successful"}
๐ MongoDB
1. CRUD Operations
from pymongo import MongoClient
client = MongoClient('mongodb://localhost:27017/')
db = client['myapp']
collection = db['users']
# Create
collection.insert_one({'name': 'John', 'age': 30, 'email': 'john@example.com'})
# Read
user = collection.find_one({'name': 'John'})
users = collection.find({'age': {'$gte': 25}})
# Update
collection.update_one(
{'name': 'John'},
{'$set': {'age': 31}}
)
# Delete
collection.delete_one({'name': 'John'})
2. Aggregation Pipeline
pipeline = [
{'$match': {'status': 'active'}}, # Filter
{'$group': {
'_id': '$department',
'avg_salary': {'$avg': '$salary'},
'count': {'$sum': 1}
}},
{'$project': {
'department': '$_id',
'avg_salary': 1,
'count': 1,
'_id': 0
}},
{'$sort': {'avg_salary': -1}}
]
results = collection.aggregate(pipeline)
3. Indexes
# Single field index
collection.create_index('email', unique=True)
# Compound index
collection.create_index([('department', 1), ('salary', -1)])
# Why indexes? Speed up queries dramatically (especially for sorting/filtering)
4. Transactions
with client.start_session() as session:
with session.start_transaction():
accounts.update_one({'_id': sender_id}, {'$inc': {'balance': -100}}, session=session)
accounts.update_one({'_id': receiver_id}, {'$inc': {'balance': 100}}, session=session)
# Both operations succeed or fail together (ACID)
๐๏ธ SQL Deep Dive
1. Joins
-- INNER JOIN: Only matching records
SELECT orders.id, customers.name
FROM orders
INNER JOIN customers ON orders.customer_id = customers.id;
-- LEFT JOIN: All from left, matching from right
SELECT customers.name, orders.id
FROM customers
LEFT JOIN orders ON customers.id = orders.customer_id;
-- SELF JOIN: Table joins itself
SELECT e1.name AS employee, e2.name AS manager
FROM employees e1
LEFT JOIN employees e2 ON e1.manager_id = e2.id;
2. Window Functions
-- Rank employees by salary within each department
SELECT
name,
department,
salary,
RANK() OVER (PARTITION BY department ORDER BY salary DESC) as rank,
AVG(salary) OVER (PARTITION BY department) as dept_avg
FROM employees;
-- Running total
SELECT
date,
revenue,
SUM(revenue) OVER (ORDER BY date) as running_total
FROM sales;
3. Common Table Expressions (CTE)
-- Better readability
WITH high_earners AS (
SELECT * FROM employees WHERE salary > 100000
),
dept_summary AS (
SELECT department, COUNT(*) as count
FROM high_earners
GROUP BY department
)
SELECT * FROM dept_summary WHERE count > 5;
-- Recursive CTE (organizational hierarchy)
WITH RECURSIVE employee_hierarchy AS (
SELECT id, name, manager_id, 1 as level
FROM employees WHERE manager_id IS NULL
UNION ALL
SELECT e.id, e.name, e.manager_id, eh.level + 1
FROM employees e
JOIN employee_hierarchy eh ON e.manager_id = eh.id
)
SELECT * FROM employee_hierarchy;
4. Indexes & Performance
-- Primary key index (automatic)
CREATE TABLE users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) UNIQUE
);
-- Composite index (for queries filtering on multiple columns)
CREATE INDEX idx_user_location ON users(city, country);
-- When to use: Frequent WHERE, JOIN, ORDER BY operations
-- Trade-off: Faster reads, slower writes
5. ACID Properties
Atomicity: All operations in transaction succeed or all fail
Consistency: Database remains in valid state
Isolation: Concurrent transactions don't interfere
Durability: Committed changes persist even after system failure
BEGIN TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT; -- Both succeed or both rollback
๐ฏ Top 25 Interview Questions - Detailed Answers
Python Questions
Q1: Explain decorators
A decorator is a function that takes another function and extends its behavior without explicitly modifying it. It's essentially a wrapper.
def authenticate(func):
def wrapper(user, *args, **kwargs):
if not user.is_authenticated:
raise PermissionError("Authentication required")
return func(user, *args, **kwargs)
return wrapper
@authenticate
def view_dashboard(user):
return f"Welcome {user.name}"
Why use them? Separation of concerns - keep authentication, logging, caching separate from business logic.
Q2: Deep copy vs Shallow copy
import copy
original = [[1, 2], [3, 4]]
shallow = copy.copy(original)
shallow[0][0] = 99 # Modifies original too!
deep = copy.deepcopy(original)
deep[0][0] = 99 # Original unchanged
Shallow: Copies object, but references nested objects
Deep: Recursively copies everything
Q3: Async vs Threading
Async (asyncio): Single-threaded concurrency for I/O-bound tasks. Better for handling many connections.
Threading: Multiple threads for I/O-bound tasks. Limited by GIL for CPU-bound work.
Multiprocessing: True parallelism for CPU-bound tasks (bypasses GIL).
When to use: Async for web servers/APIs, multiprocessing for data processing.
Django Questions
Q4: select_related vs prefetch_related
# select_related: Uses SQL JOIN (for ForeignKey/OneToOne)
Post.objects.select_related('author') # 1 query
# prefetch_related: Separate queries + Python join (for ManyToMany)
Post.objects.prefetch_related('tags') # 2 queries
Q5: When to use Signals?
Signals decouple logic that should happen "as a side effect" of an action:
Send welcome email after user creation
Clear cache after model update
Log audit trail
Avoid using signals for core business logic that should be explicit.
Q6: Middleware flow
Request โ process_request โ View โ process_response โ Response
Each middleware can short-circuit the process (e.g., authentication middleware returning 401).
Flask Questions
Q7: Purpose of Blueprints
Organize large applications into modules:
/auth (login, logout, register)
/blog (posts, comments)
/admin (dashboard, users)
Each blueprint has its own routes, templates, and static files.
Q8: Request context (g, request, session)
request: Current HTTP request data (headers, form, args)
session: Encrypted cookie for user data across requests
g: Global object for storing data during single request (like current user)
Q9: App factory pattern benefits
Multiple configurations (dev, test, production)
Easy testing with different setups
Extension initialization control
Blueprint registration in one place
FastAPI Questions
Q10: Why is FastAPI fast?
Async support: Non-blocking I/O for concurrent requests
Pydantic validation: Fast type checking at C-speed
Starlette: High-performance ASGI framework
Automatic docs: No overhead, generated from type hints
Q11: Dependency Injection pattern
async def verify_api_key(api_key: str = Header(...)):
if api_key != "secret":
raise HTTPException(status_code=403)
return api_key
@app.get("/data")
async def get_data(api_key: str = Depends(verify_api_key)):
return {"data": "sensitive info"}
Reusable, testable, clean code.
Q12: BackgroundTasks usage
Post-response work (email, logging, cleanup):
@app.post("/process")
async def process(background_tasks: BackgroundTasks):
background_tasks.add_task(heavy_computation)
return {"status": "processing"} # Immediate response
Q13: JWT Auth flow
User sends credentials โ
/tokenendpointServer validates โ returns JWT token
Client includes token in
Authorization: Bearer <token>Server validates token โ grants access
MongoDB Questions
Q14: When to use aggregation?
Complex queries requiring:
Grouping (analytics, reports)
Transformations (reshape documents)
Multi-stage filtering
Calculations across documents
Q15: Index types
Single: One field (e.g., email)
Compound: Multiple fields (e.g., department + salary)
Unique: Enforces uniqueness
Text: Full-text search
Q16: Transactions
Multi-document operations that need ACID guarantees:
Bank transfers
Order processing with inventory updates
Any operation where partial success is unacceptable
SQL Questions
Q17: Window functions
Perform calculations across rows related to current row without grouping:
Running totals
Ranking within partitions
Moving averages
Lead/lag comparisons
Q18: CTE benefits
Readability: Break complex queries into logical steps
Recursion: Handle hierarchical data
Reusability: Reference same subquery multiple times
Q19: Index strategy
Create indexes on:
Primary keys (automatic)
Foreign keys (for joins)
Columns in WHERE clauses
Columns in ORDER BY
Composite for multi-column filters
Q20: ACID ensures
Reliable transactions even with:
Concurrent users
System crashes
Network failures
Application errors
Backend General Questions
Q21: SQL vs NoSQL
SQL: Relational, structured, ACID, complex queries (financial, ERP) NoSQL: Flexible schema, horizontal scaling, eventual consistency (social media, IoT)
Q22: API Security
Authentication: JWT, OAuth2
HTTPS: Encrypted communication
Rate limiting: Prevent abuse
Input validation: SQL injection, XSS prevention
CORS: Control cross-origin requests
Q23: Dockerize application
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["uvicorn", "main:app", "--host", "0.0.0.0"]
Benefits: Consistent environments, easy deployment, isolation.
Q24: Testing endpoints
from fastapi.testclient import TestClient
client = TestClient(app)
def test_create_user():
response = client.post("/users", json={"name": "John"})
assert response.status_code == 201
assert response.json()["name"] == "John"
Q25: Scaling strategies
Horizontal: Multiple workers/instances + load balancer
Async I/O: Handle more concurrent connections
Caching: Redis for frequently accessed data
Database optimization: Indexes, query optimization, read replicas
CDN: Static assets
Message queues: Celery for background tasks
๐ก Interview Tips
Code Examples Rule
Always provide code snippets when explaining concepts. Interviewers value practical knowledge over theory.
Explain Trade-offs
For every solution, mention:
Pros: When it works best
Cons: Limitations
Alternatives: Other approaches
Real-world Context
Connect concepts to actual use cases:
"We'd use async for an API handling thousands of concurrent requests"
"Indexes are crucial here because this table has millions of rows"
Problem-solving Approach
Clarify requirements
Discuss trade-offs
Propose solution
Optimize if needed
Consider edge cases
System Design Thinking
For senior roles, show you understand:
Scalability
Performance bottlenecks
Database design
API architecture
Caching strategies
Security implications
๐ Final Preparation Checklist
[ ] Practice coding common patterns (decorators, async, ORM queries)
[ ] Review your past projects - be ready to explain architecture decisions
[ ] Prepare questions about their tech stack and challenges
[ ] Understand the company's product and technical requirements
[ ] Practice explaining concepts in simple terms (rubber duck method)
[ ] Review recent Python/framework updates
[ ] Be ready to whiteboard database schema designs
[ ] Practice SQL queries and optimization scenarios
Remember: Senior developers are evaluated on:
Problem-solving ability
Code quality and best practices
System design thinking
Communication skills
Mentorship potential
Good luck with your interview! ๐ฏ



