LearnThatStack Ace your next interview

Python.
Interview cheat sheet.

Quick reference for Python - sectioned for fast scanning. Skim the part you're shaky on, walk in confident.

Backend Development 20-section reference ~19 min read

Summary

Python is a high-level, interpreted programming language widely used for backend development due to its simplicity, extensive libraries, and strong community support. This cheatsheet covers essential Python concepts for backend development including language fundamentals, data structures, object-oriented programming, async programming, web frameworks (Flask, FastAPI, Django), database integration, caching strategies, authentication, testing, performance optimization, and common design patterns. Key features include dynamic typing, garbage collection, extensive standard library, and frameworks like Django and Flask for rapid web development.

Python Fundamentals

Variables & Data Types

# Immutable: int, float, str, tuple, frozenset
# Mutable: list, dict, set, bytearray

# Type hints (Python 3.5+)
name: str = "Alice"
age: int = 30
scores: list[int] = [90, 85, 88]

String Operations

# F-strings (Python 3.6+)
name = "Alice"
greeting = f"Hello, {name}!"

# Common methods
s = "  Hello World  "
s.strip()        # "Hello World"
s.lower()        # "  hello world  "
s.split()        # ["Hello", "World"]
" ".join(['a','b'])  # "a b"

List Comprehensions

# Basic
squares = [x**2 for x in range(10)]

# With condition
evens = [x for x in range(10) if x % 2 == 0]

# Nested
matrix = [[i*j for j in range(3)] for i in range(3)]

Dictionary Operations

# Dictionary comprehension
d = {x: x**2 for x in range(5)}

# Safe access
d.get('key', default_value)

# Merge dictionaries (Python 3.9+)
merged = dict1 | dict2

# setdefault
d.setdefault('key', []).append(value)

Data Structures

Lists

# O(1): append, pop (from end)
# O(n): insert, remove, pop(0)

lst = [1, 2, 3]
lst.append(4)       # [1, 2, 3, 4]
lst.extend([5, 6])  # [1, 2, 3, 4, 5, 6]
lst.insert(0, 0)    # [0, 1, 2, 3, 4, 5, 6]

Sets

# O(1) average: add, remove, in
set1 = {1, 2, 3}
set2 = {3, 4, 5}

set1 & set2  # Intersection: {3}
set1 | set2  # Union: {1, 2, 3, 4, 5}
set1 - set2  # Difference: {1, 2}

Deque (Double-ended queue)

from collections import deque

dq = deque([1, 2, 3])
dq.appendleft(0)    # O(1)
dq.popleft()        # O(1)

Heaps

import heapq

# Min heap by default
heap = [3, 1, 4, 1, 5]
heapq.heapify(heap)  # O(n)
heapq.heappush(heap, 2)  # O(log n)
min_val = heapq.heappop(heap)  # O(log n)

# Max heap trick
max_heap = [-x for x in values]
heapq.heapify(max_heap)

Counter

from collections import Counter

c = Counter(['a', 'b', 'a', 'c', 'b', 'a'])
c.most_common(2)  # [('a', 3), ('b', 2)]

Object-Oriented Programming

Classes & Inheritance

class Animal:
    def __init__(self, name):
        self.name = name
    
    def speak(self):
        raise NotImplementedError

class Dog(Animal):
    def speak(self):
        return f"{self.name} says Woof!"

# Multiple inheritance
class A: pass
class B: pass
class C(A, B): pass  # MRO: C -> A -> B -> object

Special Methods

class Point:
    def __init__(self, x, y):
        self.x, self.y = x, y
    
    def __repr__(self):
        return f"Point({self.x}, {self.y})"
    
    def __eq__(self, other):
        return self.x == other.x and self.y == other.y
    
    def __add__(self, other):
        return Point(self.x + other.x, self.y + other.y)

Properties

class Temperature:
    def __init__(self, celsius=0):
        self._celsius = celsius
    
    @property
    def fahrenheit(self):
        return self._celsius * 9/5 + 32
    
    @fahrenheit.setter
    def fahrenheit(self, value):
        self._celsius = (value - 32) * 5/9

Abstract Base Classes

from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self):
        pass

class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height
    
    def area(self):
        return self.width * self.height

Functions & Decorators

Function Arguments

def func(pos, /, pos_or_kw, *, kw_only, **kwargs):
    # pos: positional-only
    # pos_or_kw: positional or keyword
    # kw_only: keyword-only
    pass

# Unpacking
def func(*args, **kwargs):
    pass

func(*[1, 2, 3], **{'a': 4, 'b': 5})

Decorators

# Basic decorator
def timer(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        print(f"{func.__name__} took {time.time()-start:.4f}s")
        return result
    return wrapper

@timer
def slow_function():
    time.sleep(1)

# Decorator with arguments
def retry(max_attempts=3):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for i in range(max_attempts):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    if i == max_attempts - 1:
                        raise
        return wrapper
    return decorator

@retry(max_attempts=5)
def unstable_api_call():
    pass

Closures

def outer(x):
    def inner(y):
        return x + y  # x is captured from outer scope
    return inner

add_five = outer(5)
result = add_five(3)  # 8

Error Handling

Exception Handling

try:
    result = risky_operation()
except ValueError as e:
    # Handle specific exception
    logger.error(f"Value error: {e}")
except (TypeError, KeyError) as e:
    # Handle multiple exceptions
    logger.error(f"Type or Key error: {e}")
except Exception as e:
    # Catch all other exceptions
    logger.error(f"Unexpected error: {e}")
else:
    # Runs if no exception
    print("Success!")
finally:
    # Always runs
    cleanup()

# Raise with context
try:
    process_data()
except DataError as e:
    raise ProcessingError("Failed to process") from e

Custom Exceptions

class ValidationError(Exception):
    def __init__(self, message, code=None):
        super().__init__(message)
        self.code = code

# Usage
if not valid_email(email):
    raise ValidationError("Invalid email format", code="INVALID_EMAIL")

Concurrency & Parallelism

Threading (I/O-bound)

import threading
import concurrent.futures

# Basic thread
def worker(name):
    print(f"Worker {name} starting")
    time.sleep(2)
    print(f"Worker {name} done")

thread = threading.Thread(target=worker, args=("A",))
thread.start()
thread.join()

# Thread pool
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
    futures = [executor.submit(worker, i) for i in range(10)]
    results = [f.result() for f in futures]

Multiprocessing (CPU-bound)

import multiprocessing

def cpu_intensive(n):
    return sum(i*i for i in range(n))

# Process pool
with multiprocessing.Pool() as pool:
    results = pool.map(cpu_intensive, [1000000, 2000000, 3000000])

AsyncIO (I/O-bound, single thread)

import asyncio
import aiohttp

async def fetch_data(session, url):
    async with session.get(url) as response:
        return await response.json()

async def main():
    async with aiohttp.ClientSession() as session:
        urls = ['http://api1.com', 'http://api2.com']
        tasks = [fetch_data(session, url) for url in urls]
        results = await asyncio.gather(*tasks)
        return results

# Run async function
asyncio.run(main())

Web Frameworks

Flask (Minimal)

from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/users/<int:user_id>')
def get_user(user_id):
    user = db.get_user(user_id)
    return jsonify(user)

@app.route('/users', methods=['POST'])
def create_user():
    data = request.get_json()
    user = db.create_user(data)
    return jsonify(user), 201

if __name__ == '__main__':
    app.run(debug=True)

FastAPI (Modern, async)

from fastapi import FastAPI, HTTPException
from pydantic import BaseModel

app = FastAPI()

class User(BaseModel):
    name: str
    email: str
    age: int

@app.get("/users/{user_id}")
async def get_user(user_id: int):
    user = await db.get_user(user_id)
    if not user:
        raise HTTPException(status_code=404, detail="User not found")
    return user

@app.post("/users", response_model=User)
async def create_user(user: User):
    return await db.create_user(user.dict())
# models.py
from django.db import models

class User(models.Model):
    name = models.CharField(max_length=100)
    email = models.EmailField(unique=True)
    created_at = models.DateTimeField(auto_now_add=True)

# views.py
from django.shortcuts import get_object_or_404
from django.http import JsonResponse

def get_user(request, user_id):
    user = get_object_or_404(User, pk=user_id)
    return JsonResponse({
        'id': user.id,
        'name': user.name,
        'email': user.email
    })

RESTful APIs

REST Principles

# Resources as URLs
/users          # Collection
/users/123      # Single resource
/users/123/posts  # Sub-resource

# HTTP Methods
GET /users      # Read collection
GET /users/123  # Read single
POST /users     # Create
PUT /users/123  # Update (full)
PATCH /users/123  # Update (partial)
DELETE /users/123  # Delete

# Status Codes
200 OK          # Success
201 Created     # Resource created
204 No Content  # Success, no body
400 Bad Request # Client error
401 Unauthorized
404 Not Found
500 Internal Server Error

API Versioning

# URL versioning
@app.route('/api/v1/users')
@app.route('/api/v2/users')

# Header versioning
version = request.headers.get('API-Version', 'v1')

Pagination

@app.route('/users')
def get_users():
    page = int(request.args.get('page', 1))
    per_page = int(request.args.get('per_page', 20))
    
    users = User.query.paginate(page, per_page)
    return jsonify({
        'users': [u.to_dict() for u in users.items],
        'total': users.total,
        'page': page,
        'pages': users.pages
    })

Databases

SQL (PostgreSQL/MySQL)

import psycopg2
from contextlib import contextmanager

@contextmanager
def get_db():
    conn = psycopg2.connect("dbname=test user=postgres")
    try:
        yield conn
    finally:
        conn.close()

# Parameterized queries (prevent SQL injection)
with get_db() as conn:
    cursor = conn.cursor()
    cursor.execute(
        "SELECT * FROM users WHERE email = %s",
        (email,)
    )
    user = cursor.fetchone()

SQLAlchemy ORM

from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

Base = declarative_base()

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String(50))
    email = Column(String(120), unique=True)

# Query examples
users = session.query(User).filter(User.age > 18).all()
user = session.query(User).filter_by(email=email).first()

# Bulk operations
session.bulk_insert_mappings(User, user_data)
session.commit()

MongoDB (NoSQL)

from pymongo import MongoClient

client = MongoClient('mongodb://localhost:27017/')
db = client['mydatabase']
users = db['users']

# Insert
user_id = users.insert_one({
    'name': 'Alice',
    'email': 'alice@example.com',
    'tags': ['python', 'backend']
}).inserted_id

# Query
user = users.find_one({'email': 'alice@example.com'})
active_users = users.find({'status': 'active'}).limit(10)

# Update
users.update_one(
    {'_id': user_id},
    {'$push': {'tags': 'django'}}
)

Redis (Cache/Message Queue)

import redis

r = redis.Redis(host='localhost', port=6379, db=0)

# Cache operations
r.setex('user:123', 3600, json.dumps(user_data))  # TTL: 1 hour
cached = r.get('user:123')
if cached:
    user = json.loads(cached)

# Pub/Sub
# Publisher
r.publish('notifications', json.dumps({'type': 'user_update', 'id': 123}))

# Subscriber
pubsub = r.pubsub()
pubsub.subscribe('notifications')
for message in pubsub.listen():
    if message['type'] == 'message':
        data = json.loads(message['data'])

Caching

Caching Strategies

# Cache-aside (Lazy loading)
def get_user(user_id):
    # Check cache first
    cached = cache.get(f'user:{user_id}')
    if cached:
        return json.loads(cached)
    
    # Load from DB
    user = db.get_user(user_id)
    if user:
        cache.setex(f'user:{user_id}', 3600, json.dumps(user))
    return user

# Write-through
def update_user(user_id, data):
    # Update DB
    user = db.update_user(user_id, data)
    # Update cache
    cache.setex(f'user:{user_id}', 3600, json.dumps(user))
    return user

# Cache invalidation
def delete_user(user_id):
    db.delete_user(user_id)
    cache.delete(f'user:{user_id}')

Decorator for Caching

def cache_result(ttl=3600):
    def decorator(func):
        def wrapper(*args, **kwargs):
            cache_key = f"{func.__name__}:{str(args)}:{str(kwargs)}"
            cached = cache.get(cache_key)
            if cached:
                return json.loads(cached)
            
            result = func(*args, **kwargs)
            cache.setex(cache_key, ttl, json.dumps(result))
            return result
        return wrapper
    return decorator

@cache_result(ttl=7200)
def expensive_calculation(x, y):
    return x ** y

Authentication & Security

Password Hashing

import bcrypt

# Hash password
password = "user_password"
salt = bcrypt.gensalt()
hashed = bcrypt.hashpw(password.encode('utf-8'), salt)

# Verify password
is_valid = bcrypt.checkpw(password.encode('utf-8'), hashed)

JWT Authentication

import jwt
from datetime import datetime, timedelta

SECRET_KEY = "your-secret-key"

def generate_token(user_id):
    payload = {
        'user_id': user_id,
        'exp': datetime.utcnow() + timedelta(hours=24),
        'iat': datetime.utcnow()
    }
    return jwt.encode(payload, SECRET_KEY, algorithm='HS256')

def verify_token(token):
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
        return payload['user_id']
    except jwt.ExpiredSignatureError:
        return None
    except jwt.InvalidTokenError:
        return None

Rate Limiting

from functools import wraps
from flask import request, jsonify

def rate_limit(max_calls=100, window=3600):
    def decorator(f):
        @wraps(f)
        def wrapped(*args, **kwargs):
            key = f"rate_limit:{request.remote_addr}:{f.__name__}"
            
            try:
                current = int(cache.get(key) or 0)
                if current >= max_calls:
                    return jsonify({'error': 'Rate limit exceeded'}), 429
                
                pipe = cache.pipeline()
                pipe.incr(key)
                pipe.expire(key, window)
                pipe.execute()
                
                return f(*args, **kwargs)
            except Exception as e:
                # Log error but don't block request
                return f(*args, **kwargs)
        return wrapped
    return decorator

@app.route('/api/search')
@rate_limit(max_calls=10, window=60)
def search():
    pass

Input Validation

from marshmallow import Schema, fields, validate, ValidationError

class UserSchema(Schema):
    name = fields.Str(required=True, validate=validate.Length(min=2, max=50))
    email = fields.Email(required=True)
    age = fields.Int(required=True, validate=validate.Range(min=0, max=150))

# Usage
schema = UserSchema()
try:
    user_data = schema.load(request.json)
except ValidationError as err:
    return jsonify({'errors': err.messages}), 400

Testing

Unit Testing

import unittest
from unittest.mock import Mock, patch

class TestUserService(unittest.TestCase):
    def setUp(self):
        self.service = UserService()
    
    def test_create_user(self):
        user_data = {'name': 'Alice', 'email': 'alice@test.com'}
        user = self.service.create_user(user_data)
        self.assertEqual(user.name, 'Alice')
    
    @patch('services.database.save')
    def test_save_user(self, mock_save):
        mock_save.return_value = True
        result = self.service.save_user({'name': 'Bob'})
        self.assertTrue(result)
        mock_save.assert_called_once()

Pytest

import pytest
from pytest import fixture

@fixture
def client():
    app.config['TESTING'] = True
    with app.test_client() as client:
        yield client

def test_get_user(client):
    response = client.get('/users/1')
    assert response.status_code == 200
    assert response.json['name'] == 'Alice'

@pytest.mark.parametrize("input,expected", [
    ("hello", "HELLO"),
    ("world", "WORLD"),
    ("", ""),
])
def test_uppercase(input, expected):
    assert input.upper() == expected

Integration Testing

class TestAPI(unittest.TestCase):
    def setUp(self):
        self.app = create_app('testing')
        self.client = self.app.test_client()
        self.db = create_test_db()
    
    def tearDown(self):
        self.db.drop_all()
    
    def test_create_and_get_user(self):
        # Create user
        response = self.client.post('/users', 
            json={'name': 'Test', 'email': 'test@test.com'})
        self.assertEqual(response.status_code, 201)
        user_id = response.json['id']
        
        # Get user
        response = self.client.get(f'/users/{user_id}')
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.json['email'], 'test@test.com')

Performance Optimization

Profiling

import cProfile
import pstats

# Function profiling
def profile_func(func):
    def wrapper(*args, **kwargs):
        profiler = cProfile.Profile()
        profiler.enable()
        result = func(*args, **kwargs)
        profiler.disable()
        
        stats = pstats.Stats(profiler)
        stats.sort_stats('cumulative')
        stats.print_stats(10)  # Top 10 functions
        
        return result
    return wrapper

# Memory profiling
from memory_profiler import profile

@profile
def memory_intensive():
    large_list = [i for i in range(1000000)]
    return sum(large_list)

Database Optimization

# Use indexes
class User(Base):
    __tablename__ = 'users'
    email = Column(String, index=True)
    created_at = Column(DateTime, index=True)

# Batch operations
# Instead of:
for user in users:
    db.session.add(user)
db.session.commit()

# Do:
db.session.bulk_insert_mappings(User, users)
db.session.commit()

# Eager loading (prevent N+1 queries)
users = User.query.options(joinedload(User.posts)).all()

# Query only needed columns
users = db.session.query(User.id, User.name).all()

Caching Expensive Operations

from functools import lru_cache

@lru_cache(maxsize=128)
def expensive_computation(n):
    # Cache results in memory
    return sum(i**2 for i in range(n))

# Custom cache with TTL
from cachetools import TTLCache, cached

cache = TTLCache(maxsize=100, ttl=300)  # 5 minutes

@cached(cache)
def get_user_permissions(user_id):
    # Expensive permission calculation
    return calculate_permissions(user_id)

Design Patterns

Singleton

class Singleton:
    _instance = None
    
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

# Thread-safe singleton
import threading

class ThreadSafeSingleton:
    _instance = None
    _lock = threading.Lock()
    
    def __new__(cls):
        if not cls._instance:
            with cls._lock:
                if not cls._instance:
                    cls._instance = super().__new__(cls)
        return cls._instance

Factory Pattern

class DatabaseFactory:
    @staticmethod
    def create_database(db_type, **kwargs):
        if db_type == 'postgres':
            return PostgresDB(**kwargs)
        elif db_type == 'mysql':
            return MySQLDB(**kwargs)
        elif db_type == 'mongodb':
            return MongoDB(**kwargs)
        else:
            raise ValueError(f"Unknown database type: {db_type}")

# Usage
db = DatabaseFactory.create_database('postgres', host='localhost')

Repository Pattern

class UserRepository:
    def __init__(self, db):
        self.db = db
    
    def get_by_id(self, user_id):
        return self.db.query(User).filter_by(id=user_id).first()
    
    def get_by_email(self, email):
        return self.db.query(User).filter_by(email=email).first()
    
    def create(self, user_data):
        user = User(**user_data)
        self.db.add(user)
        self.db.commit()
        return user
    
    def update(self, user_id, updates):
        user = self.get_by_id(user_id)
        for key, value in updates.items():
            setattr(user, key, value)
        self.db.commit()
        return user

Dependency Injection

class EmailService:
    def send(self, to, subject, body):
        # Send email implementation
        pass

class UserService:
    def __init__(self, email_service: EmailService, db: Database):
        self.email_service = email_service
        self.db = db
    
    def register_user(self, user_data):
        user = self.db.create_user(user_data)
        self.email_service.send(
            user.email,
            "Welcome!",
            "Thanks for registering!"
        )
        return user

# Usage
email_service = EmailService()
db = Database()
user_service = UserService(email_service, db)

Common Interview Problems

Two Sum

def two_sum(nums, target):
    seen = {}
    for i, num in enumerate(nums):
        complement = target - num
        if complement in seen:
            return [seen[complement], i]
        seen[num] = i
    return []

LRU Cache

from collections import OrderedDict

class LRUCache:
    def __init__(self, capacity):
        self.cache = OrderedDict()
        self.capacity = capacity
    
    def get(self, key):
        if key not in self.cache:
            return -1
        self.cache.move_to_end(key)
        return self.cache[key]
    
    def put(self, key, value):
        if key in self.cache:
            self.cache.move_to_end(key)
        self.cache[key] = value
        if len(self.cache) > self.capacity:
            self.cache.popitem(last=False)

Rate Limiter (Token Bucket)

import time

class TokenBucket:
    def __init__(self, capacity, refill_rate):
        self.capacity = capacity
        self.tokens = capacity
        self.refill_rate = refill_rate
        self.last_refill = time.time()
    
    def consume(self, tokens=1):
        self.refill()
        if self.tokens >= tokens:
            self.tokens -= tokens
            return True
        return False
    
    def refill(self):
        now = time.time()
        tokens_to_add = (now - self.last_refill) * self.refill_rate
        self.tokens = min(self.capacity, self.tokens + tokens_to_add)
        self.last_refill = now

URL Shortener Design

import hashlib
import string
import random

class URLShortener:
    def __init__(self):
        self.url_map = {}
        self.reverse_map = {}
    
    def shorten(self, long_url):
        if long_url in self.reverse_map:
            return self.reverse_map[long_url]
        
        # Generate short code
        short_code = self._generate_short_code()
        while short_code in self.url_map:
            short_code = self._generate_short_code()
        
        self.url_map[short_code] = long_url
        self.reverse_map[long_url] = short_code
        return f"http://short.url/{short_code}"
    
    def expand(self, short_url):
        short_code = short_url.split('/')[-1]
        return self.url_map.get(short_code)
    
    def _generate_short_code(self, length=6):
        chars = string.ascii_letters + string.digits
        return ''.join(random.choice(chars) for _ in range(length))

Producer-Consumer Pattern

import queue
import threading

class ProducerConsumer:
    def __init__(self, max_size=10):
        self.queue = queue.Queue(maxsize=max_size)
        self.running = True
    
    def producer(self, item):
        self.queue.put(item)
        print(f"Produced: {item}")
    
    def consumer(self):
        while self.running:
            try:
                item = self.queue.get(timeout=1)
                # Process item
                print(f"Consumed: {item}")
                self.queue.task_done()
            except queue.Empty:
                continue
    
    def start_consumers(self, num_consumers=3):
        consumers = []
        for i in range(num_consumers):
            t = threading.Thread(target=self.consumer)
            t.start()
            consumers.append(t)
        return consumers

Best Practices

Code Organization

# Project structure
myproject/
├── app/
│   ├── __init__.py
│   ├── models/
│   ├── views/
│   ├── services/
│   └── utils/
├── tests/
├── config.py
├── requirements.txt
└── README.md

# Import organization
# 1. Standard library
import os
import sys
# 2. Third-party
import flask
import requests
# 3. Local application
from app.models import User
from app.utils import validate_email

Configuration Management

import os
from dotenv import load_dotenv

load_dotenv()

class Config:
    SECRET_KEY = os.environ.get('SECRET_KEY')
    DATABASE_URL = os.environ.get('DATABASE_URL')
    REDIS_URL = os.environ.get('REDIS_URL', 'redis://localhost:6379')
    
    @classmethod
    def validate(cls):
        required = ['SECRET_KEY', 'DATABASE_URL']
        missing = [var for var in required if not getattr(cls, var)]
        if missing:
            raise ValueError(f"Missing required config: {missing}")

Logging

import logging
import sys

# Configure logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('app.log'),
        logging.StreamHandler(sys.stdout)
    ]
)

logger = logging.getLogger(__name__)

# Usage
logger.info("User %s logged in", user_id)
logger.error("Failed to connect to database", exc_info=True)

Documentation

def calculate_discount(price: float, discount_percent: float) -> float:
    """
    Calculate the discounted price.
    
    Args:
        price: Original price of the item
        discount_percent: Discount percentage (0-100)
    
    Returns:
        Discounted price
    
    Raises:
        ValueError: If discount_percent is not between 0 and 100
    
    Example:
        >>> calculate_discount(100, 20)
        80.0
    """
    if not 0 <= discount_percent <= 100:
        raise ValueError("Discount must be between 0 and 100")
    return price * (1 - discount_percent / 100)

Quick Reference

Time Complexity

  • O(1): dict/set operations, list.append()
  • O(log n): binary search, heap operations
  • O(n): list traversal, dict.values()
  • O(n log n): sorting (Timsort)
  • O(n²): nested loops, bubble sort

Space Complexity

  • Be aware of hidden space usage
  • Recursion uses O(depth) stack space
  • Slicing creates new objects

Python Gotchas

# Mutable default arguments
def bad(items=[]):  # Don't do this!
    items.append(1)
    return items

def good(items=None):
    if items is None:
        items = []
    items.append(1)
    return items

# Late binding closures
funcs = []
for i in range(3):
    funcs.append(lambda: i)  # All return 2
# Fix:
funcs.append(lambda i=i: i)

# Integer caching
a = 256
b = 256
a is b  # True (cached)

a = 257
b = 257
a is b  # False (not cached)

Performance Tips

  1. Use built-in functions (they're implemented in C)
  2. Use generators for large datasets
  3. Profile before optimizing
  4. Consider using __slots__ for classes with many instances
  5. Use collections.deque for queues
  6. Prefer join() over string concatenation in loops
  7. Use set/dict for membership testing (O(1) vs O(n))

Key Concepts & Comparisons

Python Data Types & Mutability

Type Mutable Use Case Time Complexity (Access) Memory Efficiency
int, float, str No Basic data storage O(1) High
tuple No Immutable sequences, dict keys O(1) High
list Yes Dynamic arrays, stacks O(1) by index Medium
dict Yes Key-value mappings O(1) average Medium
set Yes Unique collections, fast lookups O(1) average Medium
frozenset No Immutable sets, dict keys O(1) average High

Web Framework Comparison

Framework Type Learning Curve Performance Use Case Async Support
Flask Micro Low Good Small to medium apps, APIs Limited (with extensions)
FastAPI Modern Medium Excellent High-performance APIs, async Native
Django Full-stack High Good Large applications, admin panels Limited (async views)
Tornado Async Medium Excellent Real-time applications Native
Sanic Async Medium Excellent High-performance async APIs Native

Database Integration Patterns

Pattern Implementation Use Case Pros Cons
Raw SQL psycopg2, pymysql High-performance queries Full control, optimal queries SQL injection risk, database-specific
SQLAlchemy Core Expression language Complex queries with flexibility Type safety, database agnostic Learning curve
SQLAlchemy ORM Object-relational mapping Standard CRUD operations Productivity, relationships Performance overhead
Django ORM Built-in ORM Django applications Integrated, admin interface Django-specific
Peewee Lightweight ORM Small applications Simple, lightweight Limited features

Async vs Sync Comparison

Aspect Synchronous Asynchronous Best For
Execution Model Sequential, blocking Concurrent, non-blocking I/O-bound: Async, CPU-bound: Sync
Memory Usage Higher (thread stacks) Lower (single thread) Async for many connections
Complexity Low Medium to High Sync for simple apps
Debugging Easier More complex Sync for development speed
Scalability Limited by threads High for I/O operations Async for high concurrency

Advanced Caching Strategies

Strategy Implementation Use Case Consistency Complexity
Cache-Aside App manages cache Read-heavy workloads Eventual Low
Write-Through Write to cache and DB Strong consistency needed Strong Medium
Write-Behind Async write to DB High write performance Eventual High
Refresh-Ahead Proactive cache refresh Predictable access patterns Good Medium

Python Concurrency Models

Model Mechanism Best For GIL Impact Use Cases
Threading threading module I/O-bound tasks Limited by GIL File I/O, network requests
Multiprocessing multiprocessing module CPU-bound tasks No impact Data processing, calculations
AsyncIO async/await I/O-bound, many connections Single thread Web servers, network clients
Concurrent.futures Thread/Process pools Batch processing Depends on executor Parallel task execution

Testing Strategies

Type Framework Purpose Scope Speed
Unit Tests unittest, pytest Individual functions/methods Single function Fast
Integration Tests pytest, unittest Component interactions Multiple components Medium
End-to-End Tests pytest, selenium Full application flow Entire application Slow
Performance Tests pytest-benchmark, locust Performance validation System performance Variable

Security Best Practices

Threat Mitigation Implementation Priority
SQL Injection Parameterized queries Use ORM or prepared statements Critical
XSS Input sanitization Escape user input, CSP headers High
CSRF CSRF tokens Framework middleware High
Weak Authentication Strong password hashing bcrypt, Argon2 Critical
Insecure Sessions Secure session management HTTPOnly, Secure flags High
Information Disclosure Error handling Generic error messages Medium

Performance Optimization Techniques

Technique Implementation Impact Complexity Use Case
Database Indexing Database-level indexes High Low Query optimization
Query Optimization Efficient queries, joins High Medium Database performance
Caching Redis, Memcached High Medium Frequently accessed data
Connection Pooling Database connection pools Medium Low Database connections
Lazy Loading Load data on demand Medium Medium Large datasets
Profiling cProfile, py-spy N/A Low Performance analysis

Design Pattern Applications

Pattern Python Implementation Use Case Benefits
Singleton __new__ method override Database connections Single instance globally
Factory Factory functions/classes Object creation Flexible object creation
Repository Data access abstraction Database operations Separation of concerns
Dependency Injection Constructor injection Service dependencies Testability, flexibility
Observer Event-driven programming Notifications, pub-sub Loose coupling
Strategy Algorithm abstraction Different implementations Runtime algorithm selection

Common HTTP Status Codes

Code Meaning Use Case Client Action
200 OK Successful GET, PUT Continue normally
201 Created Successful POST Resource created
204 No Content Successful DELETE No response body
400 Bad Request Invalid input Fix request format
401 Unauthorized Missing/invalid auth Provide credentials
403 Forbidden Access denied Check permissions
404 Not Found Resource doesn't exist Check URL/resource
409 Conflict Resource conflict Resolve conflict
422 Unprocessable Entity Validation failed Fix input data
500 Internal Server Error Server error Contact support

Environment & Deployment Strategies

Strategy Description Pros Cons Use Case
Virtual Environments venv, virtualenv Isolation, reproducibility Management overhead Development
Docker Containers Containerized applications Consistency, portability Learning curve Production deployment
uWSGI/Gunicorn WSGI servers Production-ready Configuration complexity Web applications
Kubernetes Container orchestration Scalability, resilience High complexity Large-scale applications
Serverless AWS Lambda, Google Cloud Functions No server management Cold starts, limitations Event-driven applications

Quick Reference & Best Practices

Essential Python Packages for Backend

Category Package Purpose Installation
Web Framework Flask Micro web framework pip install flask
Web Framework FastAPI Modern, fast API framework pip install fastapi uvicorn
Database SQLAlchemy SQL toolkit and ORM pip install sqlalchemy
Database psycopg2 PostgreSQL adapter pip install psycopg2-binary
Caching redis Redis client pip install redis
HTTP Client requests HTTP library pip install requests
Async HTTP aiohttp Async HTTP client/server pip install aiohttp
Testing pytest Testing framework pip install pytest
Validation marshmallow Serialization/validation pip install marshmallow
Environment python-dotenv Environment variables pip install python-dotenv

Performance Best Practices Checklist

✅ Database Optimization

  • Use database indexes for frequently queried columns
  • Implement connection pooling for database connections
  • Use bulk operations for multiple inserts/updates
  • Optimize queries with EXPLAIN ANALYZE
  • Implement proper database normalization

✅ Caching Strategy

  • Cache frequently accessed data (Redis/Memcached)
  • Implement cache invalidation strategies
  • Use ETags for HTTP caching
  • Cache expensive computations with @lru_cache
  • Consider CDN for static content

✅ Code Optimization

  • Use list comprehensions over loops when possible
  • Prefer generators for large datasets
  • Use __slots__ for classes with many instances
  • Profile code with cProfile before optimizing
  • Use built-in functions (implemented in C)

✅ Async Programming

  • Use async/await for I/O-bound operations
  • Implement proper connection pooling for async
  • Avoid blocking operations in async code
  • Use async context managers for resources
  • Handle exceptions properly in async code

✅ Security Implementation

  • Hash passwords with bcrypt or Argon2
  • Use parameterized queries to prevent SQL injection
  • Implement proper session management
  • Validate and sanitize all user input
  • Use HTTPS in production
  • Implement rate limiting for APIs

Common Python Gotchas & Solutions

# ❌ Mutable default arguments
def bad_function(items=[]):
    items.append(1)
    return items

# ✅ Correct approach
def good_function(items=None):
    if items is None:
        items = []
    items.append(1)
    return items

# ❌ Late binding closures
funcs = [lambda: i for i in range(3)]  # All return 2

# ✅ Correct approach
funcs = [lambda i=i: i for i in range(3)]

# ❌ Modifying list while iterating
for item in items:
    if condition:
        items.remove(item)  # Can skip elements

# ✅ Correct approach
items = [item for item in items if not condition]

Development Environment Setup

# Virtual environment
python -m venv venv
source venv/bin/activate  # Linux/Mac
venv\Scripts\activate     # Windows

# Install requirements
pip install -r requirements.txt

# Freeze dependencies
pip freeze > requirements.txt

# Run development server
export FLASK_ENV=development
flask run

# Or with FastAPI
uvicorn main:app --reload

Testing Best Practices

# ✅ Use fixtures for setup/teardown
@pytest.fixture
def client():
    app.config['TESTING'] = True
    with app.test_client() as client:
        yield client

# ✅ Test edge cases
def test_divide_by_zero():
    with pytest.raises(ZeroDivisionError):
        divide(10, 0)

# ✅ Use parametrized tests
@pytest.mark.parametrize("input,expected", [
    ("hello", "HELLO"),
    ("world", "WORLD"),
    ("", ""),
])
def test_uppercase(input, expected):
    assert input.upper() == expected

# ✅ Mock external dependencies
@patch('requests.get')
def test_api_call(mock_get):
    mock_get.return_value.json.return_value = {'status': 'ok'}
    result = make_api_call()
    assert result['status'] == 'ok'

Logging Configuration

import logging
import sys

# Production logging setup
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('app.log'),
        logging.StreamHandler(sys.stdout)
    ]
)

logger = logging.getLogger(__name__)

# Usage examples
logger.info("User %s logged in", user_id)
logger.warning("Rate limit exceeded for IP %s", ip_address)
logger.error("Database connection failed", exc_info=True)

Environment Configuration Management

# config.py
import os
from dataclasses import dataclass

@dataclass
class Config:
    SECRET_KEY: str = os.getenv('SECRET_KEY', 'dev-secret')
    DATABASE_URL: str = os.getenv('DATABASE_URL', 'sqlite:///app.db')
    REDIS_URL: str = os.getenv('REDIS_URL', 'redis://localhost:6379')
    DEBUG: bool = os.getenv('DEBUG', 'False').lower() == 'true'
    
    def __post_init__(self):
        if not self.SECRET_KEY or self.SECRET_KEY == 'dev-secret':
            raise ValueError("SECRET_KEY must be set in production")

# Usage
config = Config()

Essential Interview Topics Summary

  1. Python Fundamentals: Data types, list comprehensions, generators, decorators
  2. OOP Concepts: Classes, inheritance, polymorphism, abstract base classes
  3. Async Programming: async/await, asyncio, concurrent programming patterns
  4. Web Frameworks: Flask vs FastAPI vs Django comparison and use cases
  5. Database Integration: SQLAlchemy, raw SQL, connection pooling, transactions
  6. Caching: Redis integration, caching strategies, cache invalidation
  7. Testing: Unit testing, mocking, pytest fixtures, test-driven development
  8. Security: Authentication, authorization, input validation, password hashing
  9. Performance: Profiling, optimization techniques, database query optimization
  10. Design Patterns: Repository, Factory, Singleton, Dependency Injection

This comprehensive cheat sheet covers the essential topics for Python backend development interviews. Focus on understanding the trade-offs between different approaches and be prepared to implement these concepts with proper error handling and testing.

Found this useful? Pass it on.
Pro · $10/mo

The sheet is free. Pro goes deeper.

Pro opens the full question library behind every sheet, every refresher and a monthly AI allowance. One subscription, all formats.

Full question library All refreshers Cancel anytime