15 Python Interview Questions That Go Beyond the Basics
Some links in this article are affiliate links. We earn a commission at no extra cost to you when you purchase through them. Full disclosure.
Python interviews at the senior level donβt ask βwhatβs a list comprehension.β They test whether you understand how Python actually works under the hood. These 15 questions are the ones that matter. (Not sure Python is the right focus? See which programming language to learn for guidance.)
1. Whatβs the difference between a list and a generator?
A list stores all values in memory. A generator produces values one at a time, on demand.
# List β all 1M items in memory
squares = [x**2 for x in range(1_000_000)]
# Generator β one item at a time
squares = (x**2 for x in range(1_000_000))
When to use generators: When processing large datasets, reading files line by line, or when you donβt need all values at once.
2. Explain decorators
A decorator is a function that wraps another function to extend its behavior:
def timer(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
print(f"{func.__name__} took {time.time() - start:.2f}s")
return result
return wrapper
@timer
def slow_function():
time.sleep(1)
Follow-up: βWrite a decorator that retries a function 3 times on failure.β This tests whether you can write one, not just explain one.
3. Whatβs the GIL and why does it matter?
The Global Interpreter Lock (GIL) allows only one thread to execute Python bytecode at a time. This means CPU-bound multithreaded Python code doesnβt run in parallel.
Workarounds:
multiprocessingβ separate processes, each with its own GILasyncioβ for I/O-bound concurrency (network, file I/O)- C extensions (NumPy) β release the GIL during computation
4. Whatβs the difference between is and ==?
== checks value equality. is checks identity (same object in memory).
a = [1, 2, 3]
b = [1, 2, 3]
a == b # True β same values
a is b # False β different objects
c = a
a is c # True β same object
Gotcha: Small integers (-5 to 256) and short strings are cached, so is sometimes works by accident.
5. How does Python manage memory?
- Reference counting β each object tracks how many references point to it. When count hits 0, memory is freed.
- Garbage collector β handles circular references that reference counting canβt detect.
- Memory pools β Python pre-allocates blocks for small objects to avoid frequent system calls.
6. What are *args and **kwargs?
*args collects positional arguments into a tuple. **kwargs collects keyword arguments into a dict.
def flexible(*args, **kwargs):
print(args) # (1, 2, 3)
print(kwargs) # {'name': 'Alice'}
flexible(1, 2, 3, name='Alice')
7. Explain Pythonβs MRO (Method Resolution Order)
When a class inherits from multiple parents, Python uses C3 linearization to determine which method to call:
class A: pass
class B(A): pass
class C(A): pass
class D(B, C): pass
D.__mro__ # (D, B, C, A, object)
super() follows the MRO, not just the direct parent.
8. Whatβs the difference between @staticmethod and @classmethod?
@staticmethodβ no access to class or instance. Just a function namespaced to the class.@classmethodβ receives the class as first argument (cls). Can create instances, access class variables.
class User:
@classmethod
def from_dict(cls, data):
return cls(data['name'], data['email'])
@staticmethod
def validate_email(email):
return '@' in email
9. How does async/await work in Python?
async def creates a coroutine. await pauses execution until the awaited coroutine completes. The event loop manages switching between coroutines.
async def fetch_data():
async with aiohttp.ClientSession() as session:
resp = await session.get('https://api.example.com')
return await resp.json()
Key insight: async is for I/O-bound concurrency, not CPU-bound parallelism. For CPU work, use multiprocessing. If youβre building web APIs, see our Django vs FastAPI comparison for how async fits into framework choice.
10. What are context managers?
Context managers handle setup/teardown with with statements:
# Built-in
with open('file.txt') as f:
data = f.read()
# File automatically closed
# Custom
class Timer:
def __enter__(self):
self.start = time.time()
return self
def __exit__(self, *args):
print(f"Elapsed: {time.time() - self.start:.2f}s")
11-15: Quick-fire round
11. Mutable default arguments:
# β Bug β list is shared across calls
def add(item, lst=[]):
lst.append(item)
return lst
# β
Use None
def add(item, lst=None):
if lst is None: lst = []
lst.append(item)
return lst
12. __slots__: Restricts instance attributes to a fixed set, reducing memory usage by ~40% for classes with many instances.
13. __init__ vs __new__: __new__ creates the instance, __init__ initializes it. You rarely override __new__ unless implementing singletons or immutable types.
14. Walrus operator (:=): Assigns and returns a value in one expression: if (n := len(data)) > 10: print(n)
15. dataclasses: Auto-generate __init__, __repr__, __eq__ for data-holding classes. Use them instead of writing boilerplate. If youβre preparing for your first developer job, knowing dataclasses shows you write modern Python.
Related: Python Cheat Sheet Β· Python Complete Guide
Level up your Python: Pluralsight has Python learning paths from fundamentals to advanced topics like async, decorators, and data engineering. Start a free 10-day trial.