Python Interview Questions · India 2026

Top 70 Python Interview Questions & Answers India 2026

Complete Python interview preparation for Indian engineers — core Python, OOP, decorators, generators, GIL, asyncio, Django, and data engineering. Fresher to senior level.

✍️ Pranjal Jain, Ex-Microsoft · IIT Kanpur 📅 June 8, 2026 ⏱ 28 min read

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. [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. 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 (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
Generator expression vs list comprehension: use generators for large datasets where you don't need all values at once — memory-efficient.
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')
Also used for function forwarding and decorator patterns where you don't know the signature of the wrapped function.

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.
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
Common dunders: __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 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 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
return: Terminates the function, sends a value back, clears local state.
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 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
Use asyncio for: web servers (FastAPI, aiohttp), database async drivers (asyncpg), web scraping (aiofiles). Don't use for CPU-bound work.

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.
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 (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 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)
Structural Pattern Matching / match-case (3.10): Like switch-case but more powerful — can match object structure, lists, dicts.
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 (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.
Pranjal Jain
Pranjal Jain

Ex-Microsoft SDE · IIT Kanpur · Founder of Prepflix. Helps engineers crack product company interviews across India.