Python
04 / 04

Interview Questions

Python Interview Questions

Covering language fundamentals, memory model, OOP, async, and best practices.

1. Explain the GIL. What are its implications?

The Global Interpreter Lock (CPython) prevents multiple native threads from executing Python bytecode simultaneously. For CPU-bound work, threads don't help — use multiprocessing (separate processes) or asyncio. For I/O-bound work (network, disk), threading works because the GIL is released during I/O waits. NumPy/SciPy release the GIL for their C-level computations, enabling real parallelism.

2. is vs ==

a = [1, 2, 3]; b = [1, 2, 3]; c = a
print(a == b)   # True  — same value (__eq__)
print(a is b)   # False — different objects
print(a is c)   # True  — same object

# CPython caches small ints (-5 to 256) and some strings
x = 256; y = 256; print(x is y)   # True (cached)
x = 257; y = 257; print(x is y)   # False (not cached)

3. The mutable default argument trap

def bad(item, lst=[]):    # list created ONCE at definition
    lst.append(item)
    return lst

bad(1)   # [1]
bad(2)   # [1, 2] — unexpected shared state!

def good(item, lst=None):   # correct pattern
    if lst is None:
        lst = []
    lst.append(item)
    return lst

4. How do decorators work?

import functools

def log(func):
    @functools.wraps(func)   # preserves __name__, __doc__
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__}")
        result = func(*args, **kwargs)
        print(f"Returned {result}")
        return result
    return wrapper

@log                   # equivalent to: add = log(add)
def add(a, b):
    return a + b

add(2, 3)  # Calling add → Returned 5

5. Generator vs list comprehension

# List: eager, O(n) memory, re-iterable
squares_list = [x**2 for x in range(1000)]

# Generator: lazy, O(1) memory, single-pass
squares_gen = (x**2 for x in range(1000))

# Use generators for large sequences or pipelines
total = sum(x**2 for x in range(1_000_000))  # efficient

6. Python memory management

  • CPython uses reference counting — each object tracks the number of references pointing to it

  • When reference count reaches 0, memory is freed immediately (deterministic destruction)

  • Cyclic garbage collector handles circular references, running periodically in generations

  • __slots__ reduces memory ~40-50% for classes with many instances by avoiding per-instance __dict__

7. asyncio vs threading

  • Threading: preemptive, OS-scheduled, one thread per OS thread — limited by GIL for CPU-bound

  • asyncio: cooperative, event-loop-scheduled, single-threaded — no GIL contention, zero thread overhead

  • Blocking code in asyncio freezes the event loop — use asyncio.run_in_executor() to offload to threads

  • asyncio shines at high-concurrency I/O (thousands of simultaneous requests with minimal memory)

8. @classmethod vs @staticmethod vs instance method

class User:
    def __init__(self, name: str):
        self.name = name

    def greet(self) -> str:           # instance method — has self
        return f"Hi, {self.name}"

    @classmethod
    def from_dict(cls, data: dict) -> 'User':   # factory / alternative constructor
        return cls(data['name'])

    @staticmethod
    def validate_name(name: str) -> bool:       # utility — no self/cls
        return len(name) >= 2

9. What are Python protocols?

from typing import Protocol

class Drawable(Protocol):
    def draw(self) -> None: ...

class Circle:              # doesn't inherit from Drawable
    def draw(self) -> None:
        print("Drawing circle")

def render(shape: Drawable) -> None:
    shape.draw()

render(Circle())   # works — structural typing (duck typing + type safety)

10. What is __slots__?

class Point:
    __slots__ = ('x', 'y')   # fixed attributes, no __dict__
    def __init__(self, x, y):
        self.x = x; self.y = y

# ~40-50% less memory per instance, faster attribute access
# Trade-off: cannot add arbitrary attributes dynamically
# Best for: millions of instances (data processing, simulations)

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free