Skip to content
  • Advanced Interview Questions · Python

Advanced Interview Questions

LearnViaNotes @learnvianotes· 6 min read

The deeper questions asked for mid/senior Python roles — internals, the object model, concurrency and design — with answers you can say in under a minute.

Object model & internals

What is the difference between __new__ and __init__?

How does attribute lookup work for obj.x?

What is a descriptor? Where does Python use them?

What is a metaclass? When would you use one?

Why can't a list be a dict key, and what makes an object hashable?

What does __slots__ do and what are the trade-offs?

Functions, generators & scope

How are closures implemented?

How does a generator work under the hood?

Explain yield from.

What's the difference between an iterable and an iterator?

Concurrency & performance

If the GIL exists, why do we still need locks?

When would you choose asyncio over threads?

How would you speed up a slow Python program?

What is the difference between deepcopy and pickling for copying?

Design & practice

How do you implement a singleton in Python?

How do you make a class iterable, comparable and usable with with?

What are * and / in a function signature?

How do you manage dependencies and environments for a project?

Code you should be able to write on a whiteboard

Python — LRU cache
from collections import OrderedDict

class LRUCache:
    """O(1) get/put: OrderedDict remembers recency order."""
    def __init__(self, capacity: int):
        self.capacity = capacity
        self.data = OrderedDict()

    def get(self, key):
        if key not in self.data:
            return -1
        self.data.move_to_end(key)            # mark as most recently used
        return self.data[key]

    def put(self, key, value):
        self.data[key] = value
        self.data.move_to_end(key)
        if len(self.data) > self.capacity:
            self.data.popitem(last=False)     # evict least recently used

c = LRUCache(2)
c.put("a", 1); c.put("b", 2); c.get("a"); c.put("c", 3)
print(list(c.data), c.get("b"))               # ['a', 'c'] -1
Python — decorator with state
import functools, time

def rate_limited(max_calls, per_seconds, clock=time.monotonic):
    """Allow at most max_calls in any per_seconds window."""
    def decorator(func):
        calls = []
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            now = clock()
            while calls and now - calls[0] >= per_seconds:
                calls.pop(0)
            if len(calls) >= max_calls:
                raise RuntimeError("rate limit exceeded")
            calls.append(now)
            return func(*args, **kwargs)
        return wrapper
    return decorator

fake_now = [0.0]
@rate_limited(2, 1.0, clock=lambda: fake_now[0])
def ping():
    return "pong"

print(ping(), ping())             # pong pong
try:
    ping()
except RuntimeError as e:
    print(e)                      # rate limit exceeded
fake_now[0] = 1.5                 # time passes
print(ping())                     # pong
Python — recursive generator
def flatten(nested):
    """Flatten arbitrarily nested lists/tuples lazily."""
    for item in nested:
        if isinstance(item, (list, tuple)):
            yield from flatten(item)
        else:
            yield item

print(list(flatten([1, [2, (3, [4])], 5])))   # [1, 2, 3, 4, 5]

Free for students and teachers

Write notes like this one

Cards for every idea, quizzes and flashcards, read aloud — on the web and your phone.

Something wrong with this note? Open it to report