← Back to Interview Prep
🐍

Python Interview Q&A

50 Python scenario-based interview questions with expert answers — covering core concepts, OOP, advanced patterns, Django/Flask, and data structures.

10 Python Basics10 OOP & Classes10 Advanced Python10 Django & Flask10 DSA in Python
1

Python Basics & Memory Management

Q1.What is the difference between a list and a tuple in Python? When would you use each?

  • ▸Lists are mutable (can be changed after creation); tuples are immutable.
  • ▸Use lists for collections that need to change (e.g., adding items). Use tuples for fixed data (e.g., RGB values, DB row records).
  • ▸Tuples are faster to iterate and are hashable, so they can be used as dictionary keys.

Q2.Explain the difference between "==" and "is" in Python.

  • ▸"==" checks value equality — whether two objects have the same content.
  • ▸"is" checks identity — whether two variables point to the exact same object in memory.
  • ▸Example: a = [1,2]; b = [1,2]; a == b is True but a is b is False.

Q3.What are Python's mutable and immutable types? Why does this matter?

  • ▸Immutable: int, float, str, tuple, frozenset, bool — cannot be changed after creation.
  • ▸Mutable: list, dict, set — can be changed in place.
  • ▸Matters because: passing a mutable object to a function lets the function modify the original; immutable objects are safe from accidental mutation.

Q4.What is the difference between *args and **kwargs in Python functions?

  • ▸*args collects extra positional arguments into a tuple.
  • ▸**kwargs collects extra keyword arguments into a dictionary.
  • ▸Example: def fn(*args, **kwargs): allows calling fn(1, 2, key="val").

Q5.How does Python manage memory? What is reference counting?

  • ▸Python uses a private heap to store objects. The memory manager handles allocation.
  • ▸Reference counting tracks how many variables point to each object. When count reaches 0, memory is freed.
  • ▸CPython also uses a Cyclic Garbage Collector to handle reference cycles (e.g., A → B → A).

Q6.What is a Python generator and how is it different from a regular function?

  • ▸A generator is a function that uses "yield" instead of "return" to produce values lazily.
  • ▸It returns a generator object. Values are computed on demand — saving memory for large datasets.
  • ▸Example: def count(): yield 1; yield 2 creates a generator you iterate with next() or a for loop.

Q7.What are list comprehensions and when should you avoid them?

  • ▸List comprehensions are compact syntax to create lists: [x*2 for x in range(10)].
  • ▸Avoid when: the logic is complex (use a regular loop for readability), or when the list is huge (use a generator expression instead).

Q8.What is the Global Interpreter Lock (GIL) in Python and how does it affect multithreading?

  • ▸The GIL is a mutex in CPython that allows only one thread to execute Python bytecode at a time.
  • ▸Impact: CPU-bound tasks do NOT benefit from threading in CPython — use multiprocessing instead.
  • ▸I/O-bound tasks (file reads, HTTP calls) do benefit from threading because the GIL is released during I/O waits.

Q9.What is the difference between deepcopy and shallow copy?

  • ▸Shallow copy creates a new object but copies references to nested objects (copy.copy()).
  • ▸Deep copy creates a new object AND recursively copies all nested objects (copy.deepcopy()).
  • ▸If you shallow copy a list of lists, modifying a nested list also changes the original.

Q10.How does Python's "with" statement work and what is a context manager?

  • ▸The "with" statement calls __enter__ at the start and __exit__ at the end of the block.
  • ▸Context managers guarantee cleanup even if an exception occurs (like file.close() or connection.close()).
  • ▸Example: with open("file.txt") as f — guarantees f is closed after the block exits.
2

OOP, Classes & Design Patterns

Q11.Explain the four pillars of OOP in Python with examples.

  • ▸Encapsulation: bundling data and methods together, using private attributes (self.__name).
  • ▸Inheritance: class Dog(Animal) — Dog inherits Animal's attributes and methods.
  • ▸Polymorphism: same method name behaves differently in different classes (method overriding).
  • ▸Abstraction: hiding implementation details using abstract classes (from abc import ABC, abstractmethod).

Q12.What is the difference between @classmethod, @staticmethod, and instance methods?

  • ▸Instance method: receives self — accesses instance data. Called on an object.
  • ▸@classmethod: receives cls — accesses class-level data. Useful as alternative constructors.
  • ▸@staticmethod: receives neither. A plain function inside a class for logical grouping.

Q13.What is Method Resolution Order (MRO) in Python? How does Python handle multiple inheritance?

  • ▸MRO determines the order in which base classes are searched when calling an inherited method.
  • ▸Python uses the C3 linearization algorithm. Check it with ClassName.__mro__.
  • ▸Example: class C(A, B) searches C → A → B → object in that order.

Q14.What are Python decorators and how do you write one?

  • ▸A decorator is a function that wraps another function to extend its behavior without modifying it.
  • ▸Example: @login_required wraps a view function to check authentication first.
  • ▸Syntax: define wrapper(func), return the inner function. Use @functools.wraps to preserve the wrapped function's metadata.

Q15.What is __slots__ in Python and when should you use it?

  • ▸__slots__ = ["x", "y"] tells Python to allocate only fixed attributes instead of a dynamic __dict__.
  • ▸Benefits: reduces memory usage (no per-instance dict), slightly faster attribute access.
  • ▸Use it for classes you will instantiate millions of times (e.g., data points in a simulation).

Q16.What is the difference between __str__ and __repr__?

  • ▸__str__: user-friendly string representation shown when you print(obj).
  • ▸__repr__: developer-friendly/unambiguous representation shown in the REPL or repr(obj). Should ideally be eval()able to recreate the object.
  • ▸If __str__ is not defined, Python falls back to __repr__.

Q17.How do you implement a Singleton pattern in Python?

  • ▸Override __new__ to check if an instance already exists, and return it if so.
  • ▸Alternatively use a module-level variable (Python modules are singletons by nature).
  • ▸Thread-safe singleton uses a Lock in __new__ to prevent race conditions on first creation.

Q18.What is duck typing in Python?

  • ▸Duck typing means: "If it walks like a duck and quacks like a duck, it is a duck."
  • ▸Python does not check the type of an object — only whether it has the method/attribute being called.
  • ▸Example: any object with a .read() method can be passed to a function expecting a file, regardless of its actual type.

Q19.What is the difference between abstract classes and interfaces in Python?

  • ▸Python has abstract classes via the abc module (class Foo(ABC)) but no formal "interface" keyword.
  • ▸An abstract class can have both abstract methods (enforced) and concrete methods (shared implementation).
  • ▸Protocols (Python 3.8+) from typing allow structural subtyping — similar to interfaces in other languages.

Q20.How does Python's property decorator work?

  • ▸@property turns a method into a read-only attribute accessible as obj.value instead of obj.value().
  • ▸Use @value.setter and @value.deleter to add write and delete access.
  • ▸Useful for validation: instead of setting self._x directly, the setter can enforce constraints.
3

Advanced Python

Q21.What are Python metaclasses and what are they used for?

  • ▸A metaclass is the class of a class — it controls how classes are created.
  • ▸The default metaclass is "type". You can create custom metaclasses to add behavior to class creation.
  • ▸Use cases: ORMs (like Django models), API frameworks, automatic registration of subclasses.

Q22.Explain the difference between threading, multiprocessing, and asyncio in Python.

  • ▸threading: multiple threads in one process; share memory. Good for I/O-bound tasks. Limited by GIL for CPU tasks.
  • ▸multiprocessing: multiple processes with separate memory. True parallelism for CPU-bound tasks.
  • ▸asyncio: single-threaded event loop for I/O concurrency. Best for high-concurrency network apps (e.g., HTTP APIs with thousands of requests).

Q23.What is the difference between a generator expression and a list comprehension for memory efficiency?

  • ▸List comprehension [x**2 for x in range(1M)] creates all 1M objects in memory immediately.
  • ▸Generator expression (x**2 for x in range(1M)) yields one value at a time — constant O(1) memory.
  • ▸Always prefer generator expressions in pipelines (e.g., sum(x**2 for x in range(1M))).

Q24.How does Python's functools.lru_cache work?

  • ▸@lru_cache(maxsize=128) memoizes function results keyed by arguments.
  • ▸On repeated calls with the same arguments, it returns the cached result instead of recomputing.
  • ▸Useful for expensive pure functions. Use maxsize=None for unbounded cache. Arguments must be hashable.

Q25.What is the __call__ method in Python?

  • ▸Defining __call__ on a class makes instances callable like functions: obj().
  • ▸Useful for creating function-like objects that maintain state between calls.
  • ▸Example: a counter object that increments every time it is called.

Q26.Explain Python's data model — what are dunder methods?

  • ▸Dunder (double underscore) methods like __init__, __len__, __iter__, __add__ define how objects interact with Python syntax.
  • ▸__len__(self) makes len(obj) work. __getitem__(self, key) makes obj[key] work.
  • ▸By implementing dunders you integrate your classes with Python built-in operations and syntax.

Q27.How do you handle large CSV files in Python without running out of memory?

  • ▸Use csv.reader() as an iterator — it reads line by line without loading the whole file.
  • ▸For pandas: use pd.read_csv("file.csv", chunksize=10000) to process chunks.
  • ▸For very large files use Dask or Polars which process data in parallel/lazy chunks.

Q28.What is the difference between raise and raise Exception from e?

  • ▸"raise SomeError()" raises a new exception.
  • ▸"raise SomeError() from e" creates an exception chain — attaches the original exception as __cause__.
  • ▸This preserves the original traceback in the __context__ and makes debugging easier when re-raising.

Q29.What are Python dataclasses and when should you use them?

  • ▸@dataclass (Python 3.7+) auto-generates __init__, __repr__, and __eq__ from annotated fields.
  • ▸Use when you need a simple class to hold data without boilerplate.
  • ▸Can add __post_init__ for custom validation. Use frozen=True for immutable data objects (similar to named tuples).

Q30.How do you profile Python code to find performance bottlenecks?

  • ▸Use cProfile: python -m cProfile -s cumulative myscript.py
  • ▸Use line_profiler for line-by-line profiling: @profile decorator with kernprof.
  • ▸For memory: use memory_profiler or tracemalloc. For async code: use py-spy for sampling profiling without code modification.
4

Django & Flask

Q31.You built a Django REST API and it is very slow under load. What do you investigate first?

  • ▸Check Django Debug Toolbar or Django Silk to count SQL queries per request — N+1 queries are the most common cause.
  • ▸Use select_related() for ForeignKey and prefetch_related() for ManyToMany to batch queries.
  • ▸Add database indexes on frequently filtered/sorted fields. Cache results with Redis using Django's cache framework.

Q32.What is the N+1 query problem in Django ORM? How do you fix it?

  • ▸N+1: you fetch 100 books, then loop and do 100 separate author queries = 101 total queries.
  • ▸Fix with: Book.objects.select_related("author") — joins tables in one SQL query.
  • ▸For M2M: Book.objects.prefetch_related("tags") — does 2 queries total instead of N+1.

Q33.How do you implement background tasks in Django?

  • ▸Use Celery with a broker (Redis or RabbitMQ). Define tasks with @app.task decorator.
  • ▸Call task.delay() or task.apply_async() to queue the task without blocking the HTTP request.
  • ▸Use Django-Q or Huey as lighter alternatives to Celery for simpler use cases.

Q34.What is the difference between Flask and Django for building APIs?

  • ▸Django: batteries-included — ORM, admin, auth, forms, sessions out of the box. Best for large apps.
  • ▸Flask: micro-framework — minimal core, add only what you need. Best for small APIs or microservices.
  • ▸For REST APIs specifically: Django REST Framework (DRF) gives powerful serialization, viewsets, and auth. FastAPI (built on Starlette) is best for high-performance async APIs.

Q35.How do you secure a Django application before going to production?

  • ▸Set DEBUG=False and configure ALLOWED_HOSTS.
  • ▸Enable HTTPS and set SECURE_SSL_REDIRECT, HSTS headers, and SECURE_BROWSER_XSS_FILTER.
  • ▸Use environment variables for secrets (never commit SECRET_KEY or DB passwords to git). Enable CSRF protection and use Django's built-in password validators.

Q36.What is Flask's application context and request context?

  • ▸Application context (app_ctx): holds app-level data (current_app, g). Active per application, not per request.
  • ▸Request context (req_ctx): holds request-level data (request, session). Created for each HTTP request and destroyed when the request ends.
  • ▸g is a namespace object per request — use it to store data that should be accessible across functions during a single request.

Q37.How do you implement JWT authentication in a Python REST API?

  • ▸Use PyJWT or python-jose to encode/decode tokens. On login, issue an access token (short-lived) and a refresh token (long-lived).
  • ▸On each API request, validate the token in a middleware or decorator and extract the user identity.
  • ▸In FastAPI, use the OAuth2PasswordBearer scheme. In Django, use djangorestframework-simplejwt.

Q38.A Django app is running out of database connections in production. What do you do?

  • ▸Configure CONN_MAX_AGE in DATABASES settings to reuse connections instead of creating a new one per request.
  • ▸Use pgBouncer (connection pooler) in front of PostgreSQL to efficiently multiplex thousands of app connections into a smaller pool.
  • ▸Review long-running requests that hold connections open unnecessarily.

Q39.How do you write and run unit tests for a Django REST API?

  • ▸Use Django's TestCase (wraps each test in a transaction that is rolled back after) or APITestCase from DRF.
  • ▸Use APIClient to make fake HTTP requests: client.post("/api/endpoint/", data).
  • ▸Mock external services with unittest.mock.patch to keep tests fast and isolated.

Q40.What is middleware in Flask/Django and how does it work?

  • ▸In Django: middleware is a hook into request/response processing. Classes with process_request and process_response methods.
  • ▸In Flask: use before_request and after_request decorators, or WSGI middleware wrappers.
  • ▸Common uses: authentication checks, logging, rate limiting, CORS headers, and request ID injection.
5

Data Structures & Algorithms in Python

Q41.How do you find the two numbers in a list that add up to a target sum efficiently in Python?

  • ▸Naive O(n²): nested loops checking all pairs.
  • ▸Optimal O(n): use a set. For each number x, check if (target - x) is already in the set. If yes, found. If no, add x to set.
  • ▸def two_sum(nums, target): seen = set(); return next((x, target-x) for x in nums if (target-x) in seen or seen.add(x))
  • ▸This is the classic Two Sum problem — very common in Python interviews.

Q42.How do you reverse a string or a list in Python without using reverse()?

  • ▸Using slicing: s[::-1] creates a reversed copy in O(n).
  • ▸Using reversed() + join: "".join(reversed(s))
  • ▸In-place list reversal (two-pointer): swap lst[i] and lst[j] while i < j.

Q43.How do you find duplicates in a list in Python?

  • ▸Using collections.Counter: {k: v for k, v in Counter(lst).items() if v > 1}
  • ▸Using a set: seen = set(); {x for x in lst if x in seen or seen.add(x)}
  • ▸These run in O(n) time and O(n) space — much better than O(n²) nested loops.

Q44.How do you flatten a nested list in Python?

  • ▸For one level deep: [item for sublist in nested for item in sublist]
  • ▸For arbitrarily deep nesting: use recursion or itertools.chain.from_iterable() recursively.
  • ▸In Python 3.12+: consider itertools.chain() for shallow flattening.

Q45.How do you sort a list of dictionaries by a specific key in Python?

  • ▸sorted(dicts, key=lambda d: d["age"]) — sorts ascending by "age".
  • ▸For descending: sorted(dicts, key=lambda d: d["age"], reverse=True)
  • ▸For multiple keys: sorted(dicts, key=lambda d: (d["dept"], d["age"]))

🐍 Pro Tip for Python Interviews

Always explain time and space complexity when answering algorithm questions. Interviewers look for candidates who think about efficiency, not just correctness.

← Back to All Interview Guides