Python interview questions in 2026 cover everything from basic syntax to advanced async patterns, data structures, and OOP design. Whether you’re preparing for a junior developer role or a senior position at a FAANG company, this guide covers the most commonly asked Python interview questions with clear, concise answers.
📋 Table of Contents
Core Python Questions
1. What is the difference between a list and a tuple?
Lists are mutable — elements can be added, removed, or changed. Tuples are immutable — once created, they cannot be modified.
# List — mutable
my_list = [1, 2, 3]
my_list.append(4) # OK
my_list[0] = 10 # OK
# Tuple — immutable
my_tuple = (1, 2, 3)
# my_tuple[0] = 10 # TypeError!
# Tuples are faster and use less memory
# Use tuples for heterogeneous data (coordinates, RGB)
# Use lists for homogeneous, modifiable sequences
# Named tuples — readable tuples
from collections import namedtuple
Point = namedtuple('Point', ['x', 'y'])
p = Point(3, 4)
print(p.x, p.y) # 3 4
2. Explain Python’s GIL (Global Interpreter Lock)
The GIL is a mutex that allows only one thread to execute Python bytecode at a time, even on multi-core systems. This means Python threads cannot achieve true CPU parallelism for Python code.
- Threads release GIL during I/O operations (network, disk) — so threading works for I/O-bound tasks
- For CPU-bound parallelism, use
multiprocessing(each process has its own GIL) - Python 3.13+ has experimental free-threaded mode (GIL can be disabled)
3. What are decorators? Give an example.
import functools, time
def timer(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
elapsed = time.perf_counter() - start
print(f"{func.__name__}: {elapsed*1000:.2f}ms")
return result
return wrapper
@timer
def slow_function():
time.sleep(0.1)
return 42
slow_function() # "slow_function: 100.12ms"
4. What is the difference between *args and **kwargs?
def func(*args, **kwargs):
print(args) # tuple of positional arguments
print(kwargs) # dict of keyword arguments
func(1, 2, 3, name="Alice", age=30)
# (1, 2, 3)
# {'name': 'Alice', 'age': 30}
# Unpacking
def add(a, b, c): return a + b + c
numbers = [1, 2, 3]
print(add(*numbers)) # 6
settings = {"a": 1, "b": 2, "c": 3}
print(add(**settings)) # 6
5. What is a generator and when would you use one?
# Generator — lazy evaluation, memory efficient
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
# vs list — loads everything into memory
fib = fibonacci()
print([next(fib) for _ in range(10)])
# Use generators when:
# 1. Processing large datasets (files, DB rows)
# 2. Infinite sequences
# 3. Building pipelines
OOP Questions
6. What is the difference between @classmethod and @staticmethod?
class Circle:
pi = 3.14159
def __init__(self, radius):
self.radius = radius
# Regular method — receives instance (self)
def area(self):
return self.pi * self.radius ** 2
# classmethod — receives class (cls), can access class variables
@classmethod
def from_diameter(cls, diameter):
return cls(diameter / 2) # factory method
# staticmethod — receives nothing, utility function
@staticmethod
def is_valid_radius(radius):
return radius > 0
c1 = Circle(5)
c2 = Circle.from_diameter(10)
print(Circle.is_valid_radius(-1)) # False
7. Explain MRO (Method Resolution Order)
class A:
def method(self): return "A"
class B(A):
def method(self): return "B"
class C(A):
def method(self): return "C"
class D(B, C): # Multiple inheritance
pass
d = D()
print(d.method()) # "B" — follows MRO
print(D.__mro__) # (D, B, C, A, object)
# Python uses C3 linearization for MRO
Advanced Questions
8. How does Python’s memory management work?
- Reference counting — each object tracks how many references point to it
- When count reaches 0, object is immediately freed
- Garbage collector — handles circular references (reference counting can’t)
gc.collect()— manually trigger garbage collection- Interning — small integers (-5 to 256) and short strings are cached
9. What is the difference between shallow and deep copy?
import copy
original = [[1, 2, 3], [4, 5, 6]]
# Shallow copy — new list, but nested objects are shared
shallow = copy.copy(original) # or list(original) or original[:]
shallow[0].append(99)
print(original[0]) # [1, 2, 3, 99] — MODIFIED! (shared reference)
# Deep copy — completely independent copy
deep = copy.deepcopy(original)
deep[0].append(88)
print(original[0]) # [1, 2, 3] — unchanged
10. What are Python’s built-in data structures and their time complexities?
| Operation | list | dict | set | deque |
|---|---|---|---|---|
| Access | O(1) | O(1) avg | N/A | O(n) |
| Search | O(n) | O(1) avg | O(1) avg | O(n) |
| Insert (end) | O(1) amort | O(1) avg | O(1) avg | O(1) |
| Insert (start) | O(n) | N/A | N/A | O(1) |
| Delete | O(n) | O(1) avg | O(1) avg | O(1) |
Async Questions
11. What is the difference between asyncio.gather() and asyncio.TaskGroup?
import asyncio
# asyncio.gather — partial failures possible
async def example_gather():
results = await asyncio.gather(
task1(),
task2(),
return_exceptions=True # don't cancel others on failure
)
# Results include exceptions if return_exceptions=True
# asyncio.TaskGroup (Python 3.11+) — all-or-nothing
async def example_task_group():
async with asyncio.TaskGroup() as tg:
t1 = tg.create_task(task1())
t2 = tg.create_task(task2())
# If any task raises, all are cancelled — structured concurrency
asyncio.run(example_task_group())
Python interview success comes from understanding the language deeply, not just memorizing answers. Focus on the “why” behind each concept — why does the GIL exist, why are generators memory-efficient, why does deep copy matter. Explain trade-offs and show real-world application of each concept.
📚 You might also like
🔗 Share this article




✍️ Leave a Comment