Why Python in Indian Tech Interviews
Python is the fastest-growing language in Indian tech. It's the primary language for data engineering, ML/AI engineering, backend automation, and scripting at companies from TCS to Zepto. Even companies that primarily use Java (Amazon, Flipkart) accept Python in coding interviews. Companies that explicitly prefer Python: Razorpay, Dunzo, any data/ML-heavy roles.
Core Python (Q1–Q20)
Master these before anything else — asked at every Python interview from fresher to 3+ years experience.
Q1 · Data Types
What is the difference between a list, tuple, and set in Python?
List: Ordered, mutable, allows duplicates.
Tuple: Ordered, immutable, allows duplicates.
Set: Unordered, mutable, no duplicates.
[1, 2, 3]. O(n) search.Tuple: Ordered, immutable, allows duplicates.
(1, 2, 3). Faster and lower memory than list. Hashable (can be dict key).Set: Unordered, mutable, no duplicates.
{1, 2, 3}. O(1) lookup (hash-based). Use for membership testing and deduplication.frozenset: immutable version of set — hashable, can be a dict key or set element.
Q2 · Mutability
What is the difference between mutable and immutable types in Python?
Immutable: Cannot change after creation.
Mutable: Can be modified in-place.
Gotcha: Default mutable arguments are dangerous:
int, float, str, tuple, frozenset. When you "modify" a string, a new object is created.Mutable: Can be modified in-place.
list, dict, set, user-defined class instances.Gotcha: Default mutable arguments are dangerous:
# Bug: default list is created ONCE, shared across all calls def add_item(item, lst=[]): lst.append(item) return lst # Fix: use None as default def add_item(item, lst=None): if lst is None: lst = [] lst.append(item) return lst
Q3 · Deep Copy
What is the difference between shallow copy and deep copy in Python?
Shallow copy (
Deep copy (
list.copy(), copy.copy()): Creates a new container object, but the nested objects inside are still shared references. Modifying a nested object in the copy affects the original.Deep copy (
copy.deepcopy()): Recursively creates new copies of all nested objects. Modifying anything in the copy does NOT affect the original. More expensive.
import copy original = [[1, 2], [3, 4]] shallow = copy.copy(original) deep = copy.deepcopy(original) original[0][0] = 99 # shallow[0][0] is also 99 (shared reference!) # deep[0][0] is still 1 (independent copy)
Q4 · Comprehensions
What are list comprehensions, dict comprehensions, and generator expressions?
# List comprehension: creates a list in memory squares = [x**2 for x in range(10) if x % 2 == 0] # Dict comprehension word_len = {word: len(word) for word in ['hello', 'world']} # Generator expression: lazy evaluation, one value at a time gen = (x**2 for x in range(1000000)) # uses almost no memory
Q5 · *args / **kwargs
What are *args and **kwargs in Python?
*args: Accepts variable number of positional arguments as a tuple.**kwargs: Accepts variable number of keyword arguments as a dict.
def greet(*args, **kwargs): for name in args: print(f"Hello, {name}!") for key, val in kwargs.items(): print(f"{key}: {val}") greet('Alice', 'Bob', role='engineer', city='Bangalore')
OOP in Python (Q21–Q30)
Python OOP questions are asked at mid-level and senior interviews. Understand dunder methods, class vs instance variables, and MRO.
Q21 · Dunder Methods
What are dunder (magic) methods in Python? Give examples.
Dunder (double underscore) methods allow you to define how Python operators and built-in functions behave for your objects.
Common dunders:
class Vector: def __init__(self, x, y): self.x, self.y = x, y def __add__(self, other): return Vector(self.x+other.x, self.y+other.y) def __repr__(self): return f"Vector({self.x}, {self.y})" def __len__(self): return int((self.x**2 + self.y**2)**0.5) def __eq__(self, other): return self.x==other.x and self.y==other.y
__str__ (human-readable), __repr__ (debug repr), __len__, __iter__, __getitem__, __enter__/__exit__ (context managers), __call__ (callable objects).
Q22 · Descriptors
What is the difference between @classmethod, @staticmethod, and instance method?
Instance method: Takes
@classmethod: Takes
@staticmethod: Takes neither
self — accesses instance and class attributes. Default method type.@classmethod: Takes
cls — accesses class (not instance). Used as factory methods or to modify class-level state.@staticmethod: Takes neither
self nor cls — just a regular function scoped to the class for organizational purposes. Can't access class or instance state.
class Date: def __init__(self, day, month, year): ... @classmethod def from_string(cls, s): # factory method d, m, y = map(int, s.split('-')) return cls(d, m, y) @staticmethod def is_leap_year(year): # utility, no state needed return year % 4 == 0
Decorators & Generators (Q31–Q45)
Decorators and generators are the most-asked advanced Python topics in India. These questions come up at Razorpay, Flipkart, Swiggy, and any senior Python role.
Q31 · Decorators
What is a decorator in Python? Write one from scratch.
A decorator is a higher-order function that wraps another function to add behavior:
import functools, time def timer(func): @functools.wraps(func) # preserves __name__, __doc__ def wrapper(*args, **kwargs): start = time.perf_counter() result = func(*args, **kwargs) end = time.perf_counter() print(f"{func.__name__} took {end-start:.4f}s") return result return wrapper @timer def slow_function(): time.sleep(1)
@functools.wraps(func) is critical — without it, the wrapper replaces the original function's __name__ and __doc__ metadata.
Q32 · Generators
What is a generator in Python? How is yield different from return?
A generator is a function that uses
return: Terminates the function, sends a value back, clears local state.
yield: Pauses the function, sends a value back, preserves local state until next
yield to produce values lazily, one at a time. The function's state (local variables, execution position) is saved between calls.
def fibonacci(): a, b = 0, 1 while True: yield a # pauses here, returns a a, b = b, a + b # resumes here on next() gen = fibonacci() print(next(gen)) # 0 print(next(gen)) # 1 print(next(gen)) # 1
yield: Pauses the function, sends a value back, preserves local state until next
next() call.
Q33 · Context Managers
What is a context manager? How do you implement one?
A context manager handles resource setup and teardown using
with. Most commonly used for file I/O, database connections, and locks.
# Using class (__enter__ / __exit__) class Timer: def __enter__(self): import time self.start = time.perf_counter() return self def __exit__(self, *args): print(f"Elapsed: {time.perf_counter()-self.start:.4f}s") # Using @contextmanager decorator (simpler) from contextlib import contextmanager @contextmanager def timer(): start = time.perf_counter() yield print(f"Elapsed: {time.perf_counter()-start:.4f}s")
Concurrency: GIL, Threads, asyncio (Q46–Q55)
Concurrency questions are asked at senior levels. The GIL question is the most commonly asked Python-specific question — know it cold.
Q46 · GIL
What is the GIL? How does it affect multithreading in Python?
The Global Interpreter Lock (GIL) is a mutex in CPython that ensures only one thread executes Python bytecode at a time — even on multi-core CPUs.
Impact on CPU-bound tasks: Threading does NOT speed up CPU-bound Python code (sorting, math). Only one thread runs at a time. Use
Impact on I/O-bound tasks: The GIL is released during I/O operations (network, disk). Threading DOES speed up I/O-bound code (web scraping, API calls).
Alternatives:
Impact on CPU-bound tasks: Threading does NOT speed up CPU-bound Python code (sorting, math). Only one thread runs at a time. Use
multiprocessing instead (separate processes, each with their own GIL).Impact on I/O-bound tasks: The GIL is released during I/O operations (network, disk). Threading DOES speed up I/O-bound code (web scraping, API calls).
Alternatives:
multiprocessing for CPU parallelism, asyncio for single-threaded async I/O, C extensions (NumPy, pandas) that release the GIL internally.
Q47 · asyncio
What is asyncio in Python? When should you use it?
asyncio is Python's event loop library for asynchronous I/O — allows a single thread to handle thousands of concurrent I/O operations.async def defines a coroutine. await yields control back to the event loop while waiting for I/O (non-blocking).
import asyncio async def fetch_data(url): await asyncio.sleep(1) # simulates I/O return f"data from {url}" async def main(): results = await asyncio.gather( fetch_data("url1"), fetch_data("url2"), # runs concurrently! ) return results
Django & Flask (Q56–Q65)
Django and Flask questions are asked for backend Python roles. Django is used at larger companies; Flask at startups and microservices.
Q56 · Django vs Flask
What is the difference between Django and Flask?
Django: "Batteries included" — ORM, admin panel, auth, forms, migrations all built in. Opinionated structure. Best for large monolithic apps or when you want everything configured for you. Used at Instagram (historically), Pinterest.
Flask: Minimalist, unopinionated — just routing and request/response handling. You choose your ORM (SQLAlchemy), auth library, etc. Better for microservices and APIs where you want control. Used at Airbnb, Reddit (partially).
FastAPI: Modern, Python 3.6+, async-first, auto-generates OpenAPI docs, better performance. Increasingly preferred in India for new microservices.
Flask: Minimalist, unopinionated — just routing and request/response handling. You choose your ORM (SQLAlchemy), auth library, etc. Better for microservices and APIs where you want control. Used at Airbnb, Reddit (partially).
FastAPI: Modern, Python 3.6+, async-first, auto-generates OpenAPI docs, better performance. Increasingly preferred in India for new microservices.
Q57 · Django ORM
What is the N+1 query problem in Django? How do you fix it?
N+1 occurs when you fetch N parent objects and then make a separate DB query for each one's related objects — N+1 total queries.
# N+1 problem: 1 query for all authors + N queries for books authors = Author.objects.all() for author in authors: print(author.books.all()) # query per author! # Fix with select_related (JOINs, for ForeignKey/OneToOne) authors = Author.objects.prefetch_related('books').all() # OR for forward FK: select_related('publisher')
select_related(): SQL JOIN, for FK and one-to-one (one extra query becomes one JOIN).prefetch_related(): separate query + Python join, for many-to-many and reverse FK.
Miscellaneous Must-Know (Q66–Q70)
Q66 · Memory
How does Python's memory management and garbage collection work?
Python uses reference counting as the primary memory management strategy. Every object tracks how many references point to it. When the count drops to 0, the memory is freed immediately.
Limitation: Reference counting cannot handle circular references (object A → object B → object A). Python's cyclic garbage collector (
Memory optimization tips: Use generators instead of lists for large datasets. Use
Limitation: Reference counting cannot handle circular references (object A → object B → object A). Python's cyclic garbage collector (
gc module) periodically detects and collects these cycles.Memory optimization tips: Use generators instead of lists for large datasets. Use
__slots__ on classes to reduce per-instance memory (no __dict__). Use numpy arrays instead of Python lists for numerical data.
Q67 · Walrus Operator
What new Python features are asked in interviews (Python 3.8–3.12)?
Walrus operator := (3.8): Assigns and evaluates in one expression — useful in
Structural Pattern Matching / match-case (3.10): Like switch-case but more powerful — can match object structure, lists, dicts.
f-string = debugging (3.8):
while loops.
# Instead of: data = file.read(1024) while data: process(data); data = file.read(1024) # With walrus: while data := file.read(1024): process(data)
f-string = debugging (3.8):
f"{variable=}" prints both name and value: variable=42.
Q68 · Type Hints
What are type hints in Python and why are they important?
Type hints (
Modern Python (3.9+) uses built-in types in hints:
Type hints are now expected in production Python code at product companies. Interviewers at Razorpay, Flipkart, and Swiggy may ask you to add type hints to your code.
def greet(name: str) -> str:) add documentation-like annotations that static type checkers (mypy, pyright) use to catch type errors before runtime. They do NOT enforce types at runtime — they're hints only.Modern Python (3.9+) uses built-in types in hints:
list[int], dict[str, Any]. Use Optional[str] (= str | None) for nullable values. Union[int, str] for multiple types (or int | str in Python 3.10+).Type hints are now expected in production Python code at product companies. Interviewers at Razorpay, Flipkart, and Swiggy may ask you to add type hints to your code.
Python interview tip for India: Most Python interviewers will ask you to write code live. Practice explaining your Pythonic thinking — use list comprehensions, generators, and context managers naturally. Saying "I'd use a defaultdict here instead of checking if the key exists" signals Python expertise.