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.py
Linear 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 pointer
Run: 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 removing
Run: 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()) # 1
Run: 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 + 1
Run: 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)) # 20
Run: 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 increment
Run: 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 — lazy
Run: 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 target
Run: 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 unchanged
Run: 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): pass
Run: 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 ** 2
Run: 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: float
Run: 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 = y
Run: 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.height
Run: 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 += 1
Run: 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] = None
Run: 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"; done
python-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