A hands-on Python repo for data structures and interview topics. Every folder has runnable code, a descriptive example, and Interview Q&A in its README.
Each topic's full walkthrough and 5–6 Q&A pairs live in that folder's
README.md. Below is a quick reference with code snippets; see Most Asked Interview Questions for a master Q&A index.
Requirements: Python 3.8+ · No external dependencies
Company-wise detailed designs — each product in its own folder:
| Product | Folder |
|---|---|
| Uber | system-design/uber/ |
| system-design/instagram/ | |
| system-design/facebook/ | |
| Airbnb | system-design/airbnb/ |
| URL Shortener | system-design/url-shortener/ |
| Real-Time Coding | system-design/realtime-coding/ |
Index + building blocks: system-design/README.md
# Data structures
python3 linkedlist/caller.py
python3 stack/caller.py
python3 queues/caller.py
python3 tree/caller.py
# Python interview topic
cd python/decorator && python3 example.pyLinear collection of nodes. Each node holds data and a pointer to the next node.
from linkedlistcustom import LinkedList
ll = LinkedList()
ll.add_at_end(10)
ll.add_at_end(20)
ll.reverse() # in-place reversal
ll.detect_loop() # Floyd's cycle detection
ll.find_mid() # slow/fast pointerRun: python3 linkedlist/caller.py · Docs: linkedlist/README.md
Last In, First Out (LIFO). Array-backed with fixed capacity.
from stackcustom import Stack
stack = Stack(5)
stack.push(10)
stack.push(20)
print(stack.pop()) # 20
print(stack.get_top()) # peek without removingRun: python3 stack/caller.py · Docs: stack/README.md
First In, First Out (FIFO). Circular ring buffer.
from queuecustom import Queue
queue = Queue(5)
queue.enqueue(1)
queue.enqueue(2)
print(queue.dequeue()) # 1Run: python3 queues/caller.py · Docs: queues/README.md
Ordered tree — left subtree < node < right subtree. Supports insert, search, delete.
from binarysearchtreecustom import BinarySearchTree
bst = BinarySearchTree()
bst.add(5)
bst.add(3)
bst.add(7)
print(bst.search(3)) # True
bst.delete(3)
bst.print_tree(bst.root)Run: python3 tree/caller.py · Docs: tree/README.md
Full index: python/README.md
Wrap a function to extend behavior without changing its source.
import functools
def timer(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
print(f"{func.__name__} took {time.perf_counter() - start:.4f}s")
return result
return wrapper
@timer
def work():
...Run: python3 python/decorator/example.py
Guarantee setup/teardown with the with statement.
from contextlib import contextmanager
@contextmanager
def temp_value(obj, attr, new_value):
old = getattr(obj, attr)
setattr(obj, attr, new_value)
try:
yield obj
finally:
setattr(obj, attr, old)
with temp_value(config, "debug", True):
run_tests()Run: python3 python/context_manager/example.py
Lazy iteration with yield — constant memory for large data.
def fibonacci(limit):
a, b = 0, 1
while a < limit:
yield a
a, b = b, a + b
for n in fibonacci(100):
print(n)Run: python3 python/generator/example.py
Objects with __iter__ and __next__ — the protocol behind for loops.
class CountDown:
def __init__(self, start):
self.current = start
def __iter__(self):
return self
def __next__(self):
if self.current <= 0:
raise StopIteration
self.current -= 1
return self.current + 1Run: python3 python/iterator/example.py
Inner functions that remember variables from an enclosing scope.
def make_multiplier(factor):
def multiply(x):
return x * factor
return multiply
double = make_multiplier(2)
print(double(10)) # 20Run: python3 python/closure/example.py
Flexible signatures and argument forwarding.
def build_profile(name, **kwargs):
return {"name": name, **kwargs}
def wrapper(func, *args, **kwargs):
return func(*args, **kwargs)
build_profile("Alice", role="engineer", city="NYC")
greet(*("Hello", "Bob"), **{"punctuation": "?"})Run: python3 python/args_kwargs/example.py
Name lookup order: Local → Enclosing → Global → Built-in.
count = 0
def make_counter():
count = 0
def increment():
nonlocal count
count += 1
return count
return incrementRun: python3 python/scope/example.py
Concise way to build lists, dicts, sets, and generators.
squares = [x ** 2 for x in range(10)]
evens = [x for x in range(20) if x % 2 == 0]
word_map = {w: len(w) for w in ["python", "go"]}
total = sum(x ** 2 for x in range(1000)) # generator — lazyRun: python3 python/comprehension/example.py
Functional-style builtins (often replaced by comprehensions today).
doubled = list(map(lambda x: x * 2, numbers))
evens = list(filter(lambda x: x % 2 == 0, numbers))
for name, score in zip(names, scores):
print(name, score)Run: python3 python/lambda_map_filter/example.py
Structured error handling with try/except/else/finally.
try:
result = divide(a, b)
except ZeroDivisionError as e:
print(f"Caught: {e}")
else:
print(f"Result: {result}")
finally:
cleanup()Run: python3 python/exception_handling/example.py
Default values are evaluated once at definition time — a classic trap.
# BUG — same list shared across calls
def append_bad(item, target=[]):
target.append(item)
return target
# FIX — use None sentinel
def append_good(item, target=None):
if target is None:
target = []
target.append(item)
return targetRun: python3 python/mutable_default/example.py
== compares values; is compares object identity (same memory).
a = [1, 2, 3]
b = [1, 2, 3]
print(a == b) # True — same values
print(a is b) # False — different objects
x = None
if x is None: # always use `is` for None
...Run: python3 python/identity_equality/example.py
Shallow copy shares nested objects; deep copy is fully independent.
import copy
original = [[1, 2], [3, 4]]
shallow = copy.copy(original)
deep = copy.deepcopy(original)
original[0].append(99)
# shallow affected, deep unchangedRun: python3 python/copy_deepcopy/example.py
Magic methods control operators, printing, and protocol behavior.
class Vector:
def __init__(self, x, y):
self.x, self.y = x, y
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def __repr__(self):
return f"Vector({self.x}, {self.y})"Run: python3 python/dunder_methods/example.py
Method Resolution Order — how Python searches base classes.
class B(A):
def greet(self):
return f"B -> {super().greet()}"
class D(B, C):
pass
print(D.__mro__) # (D, B, C, A, object)Run: python3 python/inheritance_mro/example.py
Classes that create classes — control class construction.
class SingletonMeta(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super().__call__(*args, **kwargs)
return cls._instances[cls]
class Database(metaclass=SingletonMeta):
passRun: python3 python/metaclass/example.py
Validated attributes and computed fields without breaking the API.
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
return self._radius
@radius.setter
def radius(self, value):
if value <= 0:
raise ValueError("Radius must be positive")
self._radius = value
@property
def area(self):
return 3.14159 * self._radius ** 2Run: python3 python/property_descriptor/example.py
Auto-generated __init__, __repr__, and __eq__ for data objects.
from dataclasses import dataclass, field
@dataclass
class Team:
name: str
members: list[str] = field(default_factory=list)
@dataclass(frozen=True)
class Point:
x: float
y: floatRun: python3 python/dataclass/example.py
Fixed attributes — saves memory by removing per-instance __dict__.
class Point:
__slots__ = ("x", "y")
def __init__(self, x, y):
self.x = x
self.y = yRun: python3 python/slots/example.py
Enforce interfaces with @abstractmethod.
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
class Rectangle(Shape):
def area(self):
return self.width * self.heightRun: python3 python/abc/example.py
Threads share memory. The GIL limits CPU parallelism in CPython.
import threading
lock = threading.Lock()
counter = 0
def safe_increment():
global counter
with lock:
counter += 1Run: python3 python/threading/example.py
Separate processes for CPU-bound work — bypasses the GIL.
from concurrent.futures import ProcessPoolExecutor
with ProcessPoolExecutor(max_workers=4) as pool:
results = list(pool.map(square, range(8)))Run: python3 python/multiprocessing/example.py
Cooperative I/O concurrency on a single thread.
import asyncio
async def fetch(name):
await asyncio.sleep(0.1)
return f"result-{name}"
async def main():
results = await asyncio.gather(fetch("A"), fetch("B"))
print(results)
asyncio.run(main())Run: python3 python/async_await/example.py
Caching, partial application, and function utilities.
import functools
@functools.lru_cache(maxsize=128)
def fibonacci(n):
if n < 2:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
double = functools.partial(multiply, 2)Run: python3 python/functools_module/example.py
Static annotations for tooling (mypy, pyright) — not enforced at runtime.
from dataclasses import dataclass
from typing import Optional
def greet(name: str, times: int = 1) -> str:
return (f"Hello, {name}! " * times).strip()
@dataclass
class User:
id: int
name: str
email: Optional[str] = NoneRun: python3 python/type_hints/example.py
Replace dependencies in tests with fakes that record calls.
from unittest.mock import MagicMock, patch
mock_db = MagicMock()
mock_db.get.return_value = {"id": 1, "name": "Alice"}
user = fetch_user(1, mock_db)
mock_db.get.assert_called_once_with(1)Run: python3 python/unittest_mock/example.py
Reference counting + cyclic GC in CPython.
import gc, sys, weakref
obj = ["data"]
print(sys.getrefcount(obj))
weak = weakref.ref(obj)
del obj
print(weak() is None) # True — object collected
gc.collect()Run: python3 python/memory_management/example.py
Master Q&A index — full answers and examples in each folder's README.
Q: How do you reverse a linked list in O(n) time and O(1) space?
A: Three pointers (prev, current, next). Flip current.next = prev each step. → linkedlist/README.md
Q: How do you detect a cycle in a linked list?
A: Floyd's algorithm — slow (1 step) and fast (2 steps) pointers meet if cycle exists. → linkedlist/README.md
Q: Stack vs queue — key difference?
A: Stack is LIFO; queue is FIFO. → stack/README.md · queues/README.md
Q: BST average vs worst-case search time?
A: Average O(log n); worst O(n) when tree degenerates to a linked list. → tree/README.md
Q: What is a Python decorator?
A: Callable that wraps a function: @deco means func = deco(func). → python/decorator/
Q: What do __enter__ and __exit__ do?
A: Setup/teardown for with blocks. __exit__ returning True suppresses exceptions. → python/context_manager/
Q: Generator vs list comprehension?
A: Generator is lazy (constant memory). List comp builds full list in memory. → python/generator/
Q: Iterable vs iterator?
A: Iterable has __iter__(). Iterator has __iter__() + __next__(). → python/iterator/
Q: What is a closure?
A: Nested function capturing variables from enclosing scope after outer function returns. → python/closure/
Q: What are *args and **kwargs?
A: Extra positional args → tuple; extra keyword args → dict. → python/args_kwargs/
Q: Explain LEGB rule.
A: Local → Enclosing → Global → Built-in name lookup order. → python/scope/
Q: Why is def f(a=[]) dangerous?
A: Default evaluated once at definition — same mutable list shared across calls. → python/mutable_default/
Q: is vs ==?
A: == compares values; is compares identity. Use is only for None, True, False. → python/identity_equality/
Q: Shallow vs deep copy?
A: Shallow copies outer container; nested objects shared. Deep copy is fully independent. → python/copy_deepcopy/
Q: __repr__ vs __str__?
A: __repr__ is unambiguous (developers); __str__ is human-readable (users). → python/dunder_methods/
Q: What is MRO?
A: Method Resolution Order — C3 linearization for multiple inheritance lookup. → python/inheritance_mro/
Q: What is a metaclass?
A: Class of a class — controls how classes are created (type by default). → python/metaclass/
Q: How does @property work?
A: Descriptor providing getter/setter/deleter as attribute-like access. → python/property_descriptor/
Q: Dataclass vs NamedTuple?
A: Dataclass is mutable (unless frozen). NamedTuple is immutable tuple subclass. → python/dataclass/
Q: When use __slots__?
A: Many homogeneous instances where memory matters — removes per-instance __dict__. → python/slots/
Q: ABC vs duck typing?
A: ABC enforces interface at instantiation. Duck typing checks behavior at runtime. → python/abc/
Q: What is the GIL?
A: Global Interpreter Lock — one thread runs Python bytecode at a time per process. → python/threading/
Q: Threading vs multiprocessing vs async?
A: Threading: I/O-bound, shared memory. Multiprocessing: CPU-bound, bypasses GIL. Async: I/O-bound, single-thread cooperative. → python/threading/ · python/multiprocessing/ · python/async_await/
Q: Why if __name__ == "__main__" with multiprocessing?
A: Prevents infinite recursive spawning on Windows/macOS spawn start method. → python/multiprocessing/
Q: What does @lru_cache do?
A: Memoizes function results by arguments — LRU eviction when maxsize reached. → python/functools_module/
Q: Do type hints enforce types at runtime?
A: No — they're for static tools (mypy). Runtime ignores them by default. → python/type_hints/
Q: Mock vs stub?
A: Mock verifies interactions (call count/args). Stub returns canned data without verification. → python/unittest_mock/
Q: How does Python free memory?
A: Reference counting (primary) + cyclic garbage collector for circular refs. → python/memory_management/
# Data structures
for d in linkedlist stack queues tree; do python3 $d/caller.py; done
# All Python interview topics
for d in python/*/; do python3 "${d}example.py"; donepython-datastructures/
├── README.md ← you are here
├── linkedlist/ # Singly linked list
├── stack/ # LIFO stack
├── queues/ # FIFO circular queue
├── tree/ # Binary search tree
└── python/ # 27 interview topics
├── decorator/
├── context_manager/
├── generator/
├── ... (22 more)
└── README.md # topic index