Interview Prep — Python Track

Python Interview Questions

15 curated Python questions — mutability, decorators, generators, the GIL, OOP, and async. Tagged for FAANG and startups. Check off as you go.

BasicsAdvancedOOPLibraries
30Total
10Basics
10Advanced
5OOP
5Libraries
Progress saved locally. Check off questions — saves to your browser. No account needed.
0
/ 15 Done
01 What are Python's mutable and immutable data types? Python
Immutable: int, float, str, tuple, frozenset, bool — cannot be changed after creation. Changing creates a new object.
Mutable: list, dict, set — can be modified in place.

Why it matters: Immutable objects are hashable (usable as dict keys). Mutable default function arguments are a common bug — use None as default instead.
python
# Bug: mutable default argument
def bad(lst=[]): lst.append(1); return lst
# Fix:
def good(lst=None):
    if lst is None: lst = []
    lst.append(1); return lst
02 What is the difference between append() and extend()? Python
append adds its argument as one element; extend iterates it and adds each item.
a = [1, 2]
a.append([3, 4])   # [1, 2, [3, 4]]
b = [1, 2]
b.extend([3, 4])   # [1, 2, 3, 4]
b += [5]           # same as extend
03 Why is a mutable default argument a bug? Python
Defaults are evaluated once, at definition time, so every call shares one object.
def add(item, bucket=[]):     # created once
    bucket.append(item)
    return bucket

add(1)   # [1]
add(2)   # [1, 2] — not a fresh list

def add(item, bucket=None):   # the fix
    if bucket is None:
        bucket = []
04 How does slicing work, and how is it different from split()? Python
Slicing takes a range by position from any sequence and returns the same type; split() is a string method that cuts on a separator and returns a list.
s = "hello world"
s[0:5]      # "hello"   — by position
s[::-1]     # reversed
s[-5:]      # "world"   — negative indexes count from the end
s.split(" ")  # ["hello", "world"]  — by separator, gives a list

nums[::2]   # every second element — slicing works on lists too
A slice never raises for an out-of-range bound; it just returns fewer items, which is why s[:100] is safe.
05 How do you iterate with an index, or over two sequences at once? Python
enumerate for the index, zip for parallel iteration. Both are lazy, so neither builds an intermediate list.
for i, name in enumerate(names, start=1):   # start= is the useful half
    print(i, name)

for name, score in zip(names, scores):
    print(name, score)

# zip stops at the shortest input; zip(..., strict=True) raises instead (3.10+)
06 What does if __name__ == "__main__": do? Python
Python sets __name__ to "__main__" in the file it was told to run, and to the module name in anything imported. The guard therefore runs a block only on direct execution — which is what stops a script's side effects from firing when a test runner or another module imports it.
07 Explain Python decorators with an example. Python
A decorator is a function that wraps another function to add behaviour without modifying its code — uses the higher-order function concept.
python
import time

def timer(func):
    def wrapper(*args, **kwargs):
        t = time.time()
        result = func(*args, **kwargs)
        print(f"Took {time.time()-t:.3f}s")
        return result
    return wrapper

@timer
def slow_fn(): time.sleep(0.5)
Common uses: logging, authentication, caching ( @functools.lru_cache ), rate limiting.
08 What are generators in Python? How are they different from lists? Python
Generators are functions that yield values one at a time using yield , producing values lazily (on demand) instead of storing the whole sequence in memory.
python
def fibonacci():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

gen = fibonacci()
print([next(gen) for _ in range(8)])  # [0,1,1,2,3,5,8,13]
Memory: Generator uses O(1) space regardless of sequence length. A list would use O(n). Perfect for large datasets, file reading, streaming.
09 What is the GIL in Python and how does it affect multithreading? Python
The GIL (Global Interpreter Lock) is a mutex in CPython that allows only one thread to execute Python bytecode at a time, even on multi-core CPUs.
  • Problem: CPU-bound multithreaded code doesn't benefit from multiple cores
  • Not a problem for I/O-bound tasks — GIL is released during I/O operations (network, file, sleep)
  • Solution for CPU-bound: Use multiprocessing (separate processes, each with own GIL) or C extensions
Python 3.13+: Experimental "free-threaded" mode can disable the GIL.
10 What is the difference between an iterator and an iterable? Python
An iterable can produce an iterator (__iter__). An iterator produces values one at a time (__next__) and raises StopIteration when exhausted. A list is iterable but not an iterator. Iterators are single-use — which is why a generator you have already looped over yields nothing the second time.
11 What is pickling, and when should you not use it? Python
pickle serialises a Python object graph to bytes and back, preserving types that JSON cannot.
import pickle
with open("model.pkl", "wb") as fh:
    pickle.dump(obj, fh)
Never unpickle data you did not produce — the format can execute arbitrary code on load, so it is a remote code execution hole, not merely a parsing risk. It is also Python-specific and version-fragile: for anything crossing a language, a network or a long time span, use JSON or a schema format.
12 What does functools.lru_cache do, and when is it wrong? Python
It memoises a function on its arguments, so a repeat call is a dict lookup. It is wrong when the function is not pure (you cache a stale answer), when arguments are unhashable, or when the key space is unbounded — an uncapped cache keyed on user input is a memory leak. Set maxsize, and note the cache holds strong references to both arguments and results.
13 How does exception chaining work? Python
Raising inside an except block chains implicitly: the traceback shows both, joined by "During handling of the above exception". raise NewError() from err states the cause explicitly, and from None suppresses it. It matters because the wrapper alone rarely explains the failure — the original cause is the useful half.
14 How do you debug and profile a Python script? Python
For debugging, breakpoint() drops into pdb at that line (n next, s step, c continue, p expr print) — no import needed since 3.7. For profiling, start coarse and narrow down.
python -m cProfile -s cumtime script.py   # where the time goes, by function
python -m timeit -s "setup" "statement"   # micro-benchmark one line
Profile before optimising: the slow line is rarely the one you would have guessed, and cumtime points at the caller responsible rather than the leaf it bottoms out in.
15 Explain list comprehension vs map/filter vs for loop — when to use what? Python
python
nums = [1,2,3,4,5]

# List comprehension — Pythonic, readable, fast
evens = [x for x in nums if x%2==0]

# map/filter — functional, lazy, returns iterators
evens = list(filter(lambda x: x%2==0, nums))

# for loop — most readable for complex logic
evens = []
for x in nums:
    if x%2==0: evens.append(x)
Guideline: Prefer list comprehensions for simple transforms. Use for loops for complex logic. Use map / filter when chaining with itertools or for lazy evaluation.
16 What are *args and **kwargs in Python? Python
*args collects extra positional arguments as a tuple . **kwargs collects extra keyword arguments as a dict .
python
def greet(name, *args, **kwargs):
    print(f"Hello {name}")
    print(f"Extra args: {args}")
    print(f"Extra kwargs: {kwargs}")

greet("Alice", 25, "NYC", city="Boston", active=True)
Use case: Writing flexible APIs, decorators, wrapper functions.
17 What is the difference between deep copy and shallow copy? Python
Shallow copy creates a new object but inserts references to the original nested objects.
Deep copy creates a new object and recursively copies all nested objects.
python
import copy

lst = [[1, 2], [3, 4]]

shallow = copy.copy(lst)
deep = copy.deepcopy(lst)

lst[0][0] = 999
print(shallow)  # [[999, 2], [3, 4]] - affected!
print(deep)     # [[1, 2], [3, 4]] - independent
18 Explain the difference between == and is in Python. Python
== checks value equality (do they have the same content?).
is checks reference equality (are they the same object in memory?).
python
a = [1, 2, 3]
b = [1, 2, 3]
c = a

print(a == b)  # True (same values)
print(a is b)  # False (different objects)
print(a is c)  # True (same reference)
19 What is the difference between list and tuple? When would you use each? Python
Aspect List Tuple
Mutability Mutable Immutable
Syntax [1, 2, 3] (1, 2, 3)
Performance Slower Faster
Use case Dynamic data Fixed records, dict keys
20 When do you reach for defaultdict, Counter and deque? Python
defaultdict when every missing key should start from a known empty value, replacing the setdefault dance. Counter for tallying, with most_common(n) free. deque when you push and pop at both ends — O(1) at the front, where a list is O(n) because it shifts every element.
21 What does pathlib give you over os.path? Python
Paths as objects rather than strings, so joining is an operator and the operations are methods on the thing itself.
from pathlib import Path

p = Path("data") / "raw" / "input.csv"   # right separator on every OS
p.suffix, p.stem, p.parent
p.exists(), p.read_text(), p.with_suffix(".json")
for f in Path("logs").rglob("*.log"): ...
22 What are context managers and the 'with' statement? Python
Context managers handle resource setup and teardown automatically using __enter__ and __exit__ methods.
python
# Using with statement
with open("file.txt") as f:
    data = f.read()
# File automatically closed

# Custom context manager
from contextlib import contextmanager

@contextmanager
def timer():
    import time
    start = time.time()
    try:
        yield
    finally:
        print(f"Took {time.time()-start:.3f}s")
23 What is the difference between @classmethod, @staticmethod, and instance method? Python
python
class MyClass:
    count = 0

    def instance_method(self):  # access self & class
        return self

    @classmethod
    def class_method(cls):  # access class, not instance
        return cls.count

    @staticmethod
    def static_method(x):  # no self or cls
        return x * 2
Use classmethod: Factory methods, alternative constructors. Use staticmethod: Utility functions logically belonging to the class.
24 What are dunder (magic) methods in Python? Python
Dunder methods (double underscore) let you define how objects behave with built-in operators and functions.
  • __init__ — constructor
  • __str__ , __repr__ — string representation
  • __len__ len(obj)
  • __eq__ , __lt__ , __gt__ — comparison operators
  • __add__ , __mul__ — arithmetic operators
  • __getitem__ , __setitem__ — indexing obj[key]
  • __iter__ , __next__ — make object iterable
  • __enter__ , __exit__ — context manager protocol
25 Explain inheritance and the MRO (Method Resolution Order) in Python. Python
Python uses C3 linearization to determine MRO in multiple inheritance.
python
class A: pass
class B(A): pass
class C(A): pass
class D(B, C): pass

print(D.__mro__)
# D → B → C → A → object
Use help(Class) or Class.__mro__ to inspect the order.
26 What is a dataclass, and when do you still write the class by hand? Python
@dataclass generates __init__, __repr__ and __eq__ from annotated fields, with frozen=True giving immutability and a working __hash__. Write it by hand when construction needs real logic, when the class is mostly behaviour rather than data, or when the fields should stay private. For a value object it is the right default.
27 What does super() actually resolve to? Python
Not "my parent" — "the next class in this instance's MRO", which depends on the object's runtime type rather than where the call is written. That is what makes cooperative multiple inheritance work: every class calls super(), each runs once, in the linearised order you can read from Class.__mro__.
28 What is the difference between range() and xrange()? Python
Python 2:
  • range() returns a list
  • xrange() returns a generator (lazy evaluation)
Python 3:
  • range() behaves like xrange() (returns a range object, lazy)
  • xrange() doesn't exist
Tip: In Python 3, range() is memory-efficient regardless of size.
29 How do you handle exceptions in Python? Explain try, except, else, finally. Python
python
try:
    result = 10 / 0
except ZeroDivisionError:
    print("Cannot divide by zero")
else:
    print(f"Result: {result}")  # runs if no exception
finally:
    print("Cleanup complete")  # always runs
  • try: Code that might raise an exception
  • except: Handle specific exceptions
  • else: Runs if no exception occurred
  • finally: Always executes (cleanup code)
30 Find the maximum profit by buying and selling stock at different times. Python
Problem: Given stock prices with timestamps, find the maximum profit by buying at one price and selling at a later higher price.
python
prices = [
    ("2026-03-29 09:30:00", 150.25),
    ("2026-03-29 09:31:00", 152.10),
    ("2026-03-29 09:32:00", 151.80),
    ("2026-03-29 09:33:00", 154.50)
]

max_profit = 0
min_price = prices[0][1]

for time, price in prices[1:]:
    profit = price - min_price
    max_profit = max(max_profit, profit)
    min_price = min(min_price, price)

print(f"{max_profit:.2f}")  # 4.25
Explanation:
  • Track running minimum price seen so far
  • Calculate profit if selling at current price
  • Update max_profit and min_price as we iterate
Complexity:
  • Time: O(n) — single pass through the array
  • Space: O(1) — only two variables tracked
Pattern: This is the classic "Best Time to Buy and Sell Stock" interview problem. The key insight is that you must buy before you sell, so we track the minimum price seen so far and calculate potential profit at each step.
No questions match.