Python Master Reference Guide Complete
Python Master Reference Guide Complete
Topics Covered:
Python Fundamentals • Data Structures • OOP
Functional & Dynamic Programming • Memory Management
Data Manipulation & Mining • Algorithms & Complexity
Advanced Topics • LeetCode Patterns & Brute Force
# Multiple assignment
x, y, z = 1, 2, 3
a = b = c = 0
# Type casting
num_str = str(42) # '42'
num_int = int('99') # 99
num_float = float('3.14') # 3.14
to_bool = bool(0) # False (0, '', [], None are falsy)
# Strings
s = 'hello'
s2 = "world"
s3 = '''multi
line'''
s4 = f'Hello {name}, you are {age}' # f-string
s5 = [Link]() # HELLO
s6 = s[1:4] # ell (slicing)
s7 = s[::-1] # olleh (reverse)
s8 = ' hi '.strip() # 'hi'
s9 = 'a,b,c'.split(',') # ['a','b','c']
s10 = '-'.join(['a','b']) # 'a-b'
# Numbers
i = 10 // 3 # 3 (floor division)
r = 10 % 3 # 1 (modulo)
p = 2 ** 10 # 1024 (power)
import math
[Link](16) # 4.0
[Link](2.1) # 3
[Link](2.9) # 2
abs(-5) # 5
1.3 Operators
# Arithmetic: + - * / // % **
# Comparison: == != < > <= >=
# Logical: and or not
# Bitwise: & | ^ ~ << >>
# Assignment: = += -= *= /= //= **= %=
# Identity: is is not
# Membership: in not in
x = 5
print(x & 3) # 1 (bitwise AND: 101 & 011 = 001)
print(x | 3) # 7 (bitwise OR: 101 | 011 = 111)
print(x ^ 3) # 6 (XOR: 101 ^ 011 = 110)
print(x << 1) # 10 (left shift = multiply by 2)
print(x >> 1) # 2 (right shift = floor divide by 2)
# Ternary
result = 'pass' if score >= 60 else 'fail'
# for loop
for i in range(5): # 0 1 2 3 4
print(i, end=' ')
# while loop
n = 0
while n < 5:
n += 1
if n == 3: continue # skip 3
if n == 5: break # stop at 5
print(n, end=' ') # 1 2 4
1.5 Comprehensions
# List comprehension
squares = [x**2 for x in range(10)]
evens = [x for x in range(20) if x % 2 == 0]
flat = [x for row in [[1,2],[3,4]] for x in row] # [1,2,3,4]
# Dict comprehension
word_len = {w: len(w) for w in ['hi','hello','hey']}
# {'hi': 2, 'hello': 5, 'hey': 3}
# Set comprehension
unique_sq = {x**2 for x in [-2,-1,0,1,2]} # {0,1,4}
lst = [3, 1, 4, 1, 5, 9, 2, 6]
# Mutation
[Link](7) # add to end
[Link](0, 0) # insert at index
[Link]([8,9]) # add multiple
[Link]() # remove last, returns it
[Link](0) # remove at index
[Link](1) # remove first occurrence of value
del lst[2:4] # delete slice
# Useful operations
[Link]() # in-place sort O(n log n)
[Link](key=lambda x: -x) # sort descending
sorted_copy = sorted(lst) # returns new list
[Link]() # in-place reverse
[Link](1) # count occurrences
[Link](9) # find first index of value
[Link]() # shallow copy
2.2 Tuples
Ordered, immutable, hashable (can be used as dict keys or in sets).
t = (1, 2, 3)
t2 = 1, 2, 3 # parentheses optional
single = (42,) # trailing comma needed for single-element
# Unpacking
a, b, c = t # a=1, b=2, c=3
first, *rest = t # first=1, rest=[2,3]
# Named tuples
from collections import namedtuple
Point = namedtuple('Point', ['x', 'y'])
p = Point(3, 4)
print(p.x, p.y) # 3 4
print(p[0], p[1]) # 3 4 (also indexable)
2.3 Sets
Unordered, unique elements. O(1) average lookup/insert/delete. Great for deduplication.
s = {1, 2, 3, 4, 5}
[Link](6) # add element
[Link](10) # remove if present (no error)
[Link](3) # remove (raises KeyError if missing)
# Set operations
a = {1, 2, 3, 4}
b = {3, 4, 5, 6}
a | b # union: {1,2,3,4,5,6}
a & b # intersection: {3,4}
a - b # difference: {1,2}
a ^ b # symmetric diff: {1,2,5,6}
[Link](b) # False
[Link]({1,2}) # True
# Deduplication
nums = [1, 2, 2, 3, 3, 3]
unique = list(set(nums)) # [1, 2, 3] (order not guaranteed)
2.4 Dictionaries
Key-value store. O(1) average access, insert, delete. Ordered by insertion order (Python 3.7+).
# Access
d['name'] # 'Alice' (KeyError if missing)
[Link]('age', 0) # 30 (default 0 if missing)
[Link]('city', 'NYC') # sets if missing, returns value
# Modification
d['age'] = 31 # update
[Link]({'age': 32, 'x': 1}) # merge another dict
del d['x'] # delete key
[Link]('city', None) # remove and return (no error if missing)
# Iteration
[Link]() # dict_keys([...])
[Link]() # dict_values([...])
[Link]() # dict_items([(k,v), ...])
for k, v in [Link]():
print(f'{k}: {v}')
# Min-heap (default)
h = []
[Link](h, 5)
[Link](h, 1)
[Link](h, 3)
[Link](h) # 1 (always removes minimum)
h[0] # peek minimum without removing
# k smallest / k largest
[Link](3, [5,3,1,7,2]) # [1,2,3]
[Link](3, [5,3,1,7,2]) # [7,5,3]
class LinkedList:
def __init__(self):
[Link] = None
def to_list(self):
result, cur = [], [Link]
while cur:
[Link]([Link])
cur = [Link]
return result
2.8 Trees
# Binary Tree Node
class TreeNode:
def __init__(self, val=0, left=None, right=None):
[Link] = val
[Link] = left
[Link] = right
# ── Traversals (DFS) ──
def inorder(root): # Left → Node → Right
return inorder([Link]) + [[Link]] + inorder([Link]) if root else []
# ── Tree Height ──
def height(root):
if not root: return 0
return 1 + max(height([Link]), height([Link]))
# ── BST operations ──
def bst_search(root, target):
if not root: return False
if [Link] == target: return True
if target < [Link]: return bst_search([Link], target)
return bst_search([Link], target)
2.9 Graphs
# Adjacency list representation
graph = {
0: [1, 2],
1: [0, 3, 4],
2: [0, 5],
3: [1],
4: [1, 5],
5: [2, 4]
}
# DFS — iterative
def dfs_iterative(graph, start):
visited, stack = set(), [start]
order = []
while stack:
node = [Link]()
if node not in visited:
[Link](node)
[Link](node)
[Link](graph[node])
return order
# DFS — recursive
def dfs_recursive(graph, node, visited=None):
if visited is None: visited = set()
[Link](node)
for neighbor in graph[node]:
if neighbor not in visited:
dfs_recursive(graph, neighbor, visited)
return visited
📋 CHEAT SHEET: list O(1) append/access | dict/set O(1) avg lookup | deque O(1) both ends | heap
O(log n) push/pop | heapify O(n)
SECTION 3: Object-Oriented Programming (OOP)
3.1 Classes & Objects
class Animal:
# Class variable (shared across ALL instances)
kingdom = 'Animalia'
count = 0
def speak(self):
return f'{[Link]} makes a sound'
@classmethod
def get_count(cls):
return [Link]
@staticmethod
def is_alive(age):
return age > 0
def __repr__(self):
return f'Animal(name={[Link]!r}, age={[Link]})'
a1 = Animal('Dog', 3)
a2 = Animal('Cat', 5)
print(Animal.get_count()) # 2
print(Animal.is_alive(3)) # True
print(repr(a1)) # Animal(name='Dog', age=3)
class Dog(Animal):
def speak(self): # override
return f'{[Link]} says: Woof!'
def fetch(self):
return f'{[Link]} fetches the ball'
class Cat(Animal):
def speak(self): # override
return f'{[Link]} says: Meow!'
# Multiple inheritance
class Robot:
def charge(self):
return 'Charging...'
rd = RoboDog('Rex')
[Link]() # 'Rex says: Woof!' (from Dog)
[Link]() # 'Charging...' (from Robot)
# Polymorphism
animals = [Dog('Buddy'), Cat('Whiskers'), Dog('Max')]
for animal in animals:
print([Link]()) # each calls its own version
@abstractmethod
def perimeter(self): pass
class Circle(Shape):
def __init__(self, radius):
self._radius = radius # _name = 'protected' (convention)
self.__secret = 'hidden' # __name = 'private' (name mangling)
@property
def radius(self): # getter
return self._radius
@[Link]
def radius(self, value): # setter with validation
if value <= 0: raise ValueError('Radius must be positive')
self._radius = value
def area(self):
import math
return [Link] * self._radius ** 2
def perimeter(self):
import math
return 2 * [Link] * self._radius
c = Circle(5)
print([Link]()) # Area: 78.54, Perimeter: 31.42
[Link] = 10 # uses setter
# c.__secret # AttributeError — name mangled to _Circle__secret
# String representations
def __str__(self): return f'Vector({self.x}, {self.y})'
def __repr__(self): return f'Vector(x={self.x}, y={self.y})'
# Arithmetic operators
def __add__(self, other): return Vector(self.x+other.x, self.y+other.y)
def __sub__(self, other): return Vector(self.x-other.x, self.y-other.y)
def __mul__(self, scalar): return Vector(self.x*scalar, self.y*scalar)
def __rmul__(self, scalar): return self.__mul__(scalar)
# Comparison
def __eq__(self, other): return self.x == other.x and self.y == other.y
def __lt__(self, other): return abs(self) < abs(other)
# Container protocol
def __len__(self): return 2
def __getitem__(self, i): return (self.x, self.y)[i]
def __iter__(self): return iter((self.x, self.y))
# Boolean / numeric
def __bool__(self): return self.x != 0 or self.y != 0
def __abs__(self): return (self.x**2 + self.y**2) ** 0.5
def __hash__(self): return hash((self.x, self.y))
c = make_counter(10)
c() # 11
c() # 12
def timer(func):
@[Link](func) # preserves original __name__, __doc__
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
end = time.perf_counter()
print(f'{func.__name__} took {end-start:.4f}s')
return result
return wrapper
@timer
def slow_sum(n):
return sum(range(n))
@repeat(3)
def greet(name):
print(f'Hello, {name}!')
4.3 Generators
# Generator function — uses yield
def fibonacci():
a, b = 0, 1
while True: # infinite sequence!
yield a
a, b = b, a + b
fib = fibonacci()
print([next(fib) for _ in range(10)]) # [0,1,1,2,3,5,8,13,21,34]
# Generator expression
gen = (x**2 for x in range(1000000))
next(gen) # 0 — computes lazily!
@lru_cache(maxsize=None)
def fib_memo(n):
if n <= 1: return n
return fib_memo(n-1) + fib_memo(n-2)
# Manual memoization
def fib_manual(n, memo={}):
if n in memo: return memo[n]
if n <= 1: return n
memo[n] = fib_manual(n-1, memo) + fib_manual(n-2, memo)
return memo[n]
import sys
# Every Python object has: identity (id), type, value, reference count
x = [1, 2, 3]
y = x # y and x reference the SAME object on the heap
id(x) == id(y) # True
# Object sizes
[Link](42) # 28 bytes (int)
[Link]('hello') # 54 bytes
[Link]([1,2,3]) # 88 bytes (list header + 3 pointers)
[Link]({}) # 232 bytes (dict is expensive!)
[Link](set()) # 216 bytes
# Reference counting
a = [1, 2, 3] # ref count = 1
b = a # ref count = 2
c = a # ref count = 3
del b # ref count = 2
del c # ref count = 1
del a # ref count = 0 → object is freed immediately
a = Node()
b = Node()
[Link] = b # a → b
[Link] = a # b → a (cycle!)
del a # ref count stays 1 each, NOT freed
del b # same — gc module cleans these up
# Garbage collector handles cycles
[Link]() # force collection
gc.get_count() # (gen0, gen1, gen2) object counts
[Link]() # disable cyclic GC (dangerous!)
[Link]() # re-enable
snapshot = tracemalloc.take_snapshot()
top_stats = [Link]('lineno')
for stat in top_stats[:3]:
print(stat)
[Link]()
class WithSlots:
__slots__ = ['x', 'y']
def __init__(self, x, y): self.x = x; self.y = y
import sys
d = WithDict(1, 2); s = WithSlots(1, 2)
[Link](d.__dict__) # 232 bytes
# WithSlots has no __dict__ — saves ~200 bytes per instance
# Writing files
with open('[Link]', 'w') as f: # overwrite
[Link]('Hello\n')
# CSV
import csv
with open('[Link]', 'r') as f:
reader = [Link](f) # each row = dict
rows = list(reader)
# JSON
import json
with open('[Link]', 'r') as f:
data = [Link](f) # file → Python object
6.2 Regex
import re
# Replace
clean = [Link](r'\s+', ' ', 'too many spaces') # 'too many spaces'
# Split
[Link](r'[,;\s]+', 'a, b;c d') # ['a','b','c','d']
# Groups
m = [Link](r'(\w+)@(\w+)\.(\w+)', text)
[Link](0) # 'alice@[Link]'
[Link](1) # 'alice'
[Link](2) # 'example'
# Common patterns
r'\d+' # one or more digits
r'\w+' # word characters (letters, digits, _)
r'\s+' # whitespace
r'^...$' # anchors: start and end of string
r'[a-zA-Z]+' # letters only
r'(?i)pattern' # case-insensitive
6.3 NumPy
import numpy as np
# Creating arrays
a = [Link]([1, 2, 3, 4, 5])
b = [Link]((3, 4)) # 3x4 matrix of zeros
c = [Link]((2, 3)) # 2x3 matrix of ones
d = [Link](0, 10, 2) # [0 2 4 6 8]
e = [Link](0, 1, 5) # [0. .25 .5 .75 1.]
r = [Link](3, 3) # 3x3 random floats
# Matrix operations
A = [Link]([[1,2],[3,4]])
B = [Link]([[5,6],[7,8]])
A @ B # matrix multiply [[19,22],[43,50]]
A.T # transpose [[1,3],[2,4]]
[Link](A, B) # same as A @ B
# Statistics
[Link](arr) # mean
[Link](arr) # standard deviation
[Link](arr) # minimum
[Link](arr) # index of maximum
[Link](arr, axis=0) # sum along columns
[Link](arr, axis=1) # sort each row
6.4 Pandas
import pandas as pd
# Create DataFrame
df = [Link]({
'name': ['Alice','Bob','Charlie','Diana'],
'age': [25, 30, 35, 28],
'score':[95, 80, 85, 92]
})
# Inspection
[Link](2) # first 2 rows
[Link](2) # last 2 rows
[Link]() # dtypes, nulls
[Link]() # statistics
[Link] # (4, 3)
[Link] # Index(['name','age','score'])
# Selection
df['name'] # Series (column)
df[['name','score']] # DataFrame (multiple columns)
[Link][0] # row 0 by index
[Link][1:3, 0:2] # rows 1-2, cols 0-1
[Link][df['age'] > 28] # filter rows
# Filtering
df[df['score'] >= 90]
df[(df['age'] > 25) & (df['score'] > 85)]
# GroupBy
[Link]('grade')['score'].mean()
[Link]('grade').agg({'score': ['mean','max'], 'age': 'min'})
# Handling nulls
[Link]().sum() # count nulls per column
[Link]() # drop rows with any null
[Link](0) # fill nulls with 0
df['age'].fillna(df['age'].mean(), inplace=True)
# Merge / Join
df2 = [Link]({'name':['Alice','Bob'], 'dept':['Eng','HR']})
merged = [Link](df, df2, on='name', how='left') # left join
# Sort
df.sort_values('score', ascending=False)
df.sort_values(['grade','score'], ascending=[True, False])
SECTION 7: Algorithms & Complexity
7.1 Big O Notation
Big O describes worst-case time or space growth as input size n grows. Always analyze the dominant
term.
# Example analysis
def example(lst): # O(n^2)
for i in range(len(lst)): # O(n)
for j in range(len(lst)): # O(n)
pass
# Space complexity
def reverse(lst): # O(n) space
return lst[::-1] # creates new list
7.5 Backtracking
# Template for backtracking
def backtrack(state, choices):
if is_solution(state):
record(state)
return
for choice in choices:
if is_valid(choice, state):
make_choice(state, choice)
backtrack(state, next_choices(state))
undo_choice(state, choice) # KEY: backtrack
# Permutations of a list
def permutations(nums):
result = []
def bt(path, remaining):
if not remaining:
[Link](path[:]); return
for i, num in enumerate(remaining):
[Link](num)
bt(path, remaining[:i] + remaining[i+1:])
[Link]()
bt([], nums)
return result
# N-Queens
def solve_n_queens(n):
result = []
cols = set(); diag1 = set(); diag2 = set()
board = [['.']*n for _ in range(n)]
def bt(row):
if row == n:
[Link]([''.join(r) for r in board]); return
for col in range(n):
if col in cols or (row-col) in diag1 or (row+col) in diag2: continue
[Link](col); [Link](row-col); [Link](row+col)
board[row][col] = 'Q'
bt(row + 1)
board[row][col] = '.'
[Link](col); [Link](row-col); [Link](row+col)
bt(0)
return result
SECTION 8: Advanced Topics
8.1 Type Hints
from typing import List, Dict, Tuple, Optional, Union, Callable, Any, TypeVar
from typing import Set, FrozenSet, Sequence, Iterable, Generator
# Basic annotations
def greet(name: str) -> str:
return f'Hello, {name}'
# Callable type
def apply(func: Callable[[int, int], int], a: int, b: int) -> int:
return func(a, b)
with Timer() as t:
total = sum(range(1_000_000))
# Elapsed: 0.0312s
@contextmanager
def managed_resource(name):
print(f'Opening {name}')
try:
yield name # everything before yield = __enter__
except Exception as e:
print(f'Error: {e}')
finally:
print(f'Closing {name}') # always runs = __exit__
# Basic coroutine
async def fetch_data(url: str) -> str:
await [Link](1) # non-blocking wait (simulating I/O)
return f'Data from {url}'
[Link](main())
results = [0] * 5
threads = [[Link](target=worker, args=(i, results, i)) for i in range(5)]
for t in threads: [Link]()
for t in threads: [Link]()
print(results) # [0, 1, 4, 9, 16]
# ProcessPoolExecutor
with [Link]() as ex:
results = list([Link](cpu_task, [1000, 2000, 3000]))
# Custom metaclass
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):
def __init__(self):
[Link] = 'connected'
db1 = Database()
db2 = Database()
db1 is db2 # True — same instance!
# __new__ vs __init__
class ImmutablePoint:
def __new__(cls, x, y): # controls object creation
instance = super().__new__(cls)
return instance
def __init__(self, x, y): # initializes object
self._x = x; self._y = y
def __setattr__(self, name, val):
if [Link]('_'):
super().__setattr__(name, val)
else:
raise AttributeError('Immutable!')
SECTION 9: Built-in Python Tools
9.1 collections Module
from collections import Counter, defaultdict, OrderedDict, deque, namedtuple
# namedtuple
Point = namedtuple('Point', ['x','y'])
p = Point(3, 4)
p.x, p.y # 3, 4
p._asdict() # OrderedDict([('x',3),('y',4)])
p._replace(x=10) # Point(x=10, y=4)
9.2 itertools
import itertools
list([Link]('ABC', 2))
# [('A','B'),('A','C'),('B','A'),...] — 6 total
list([Link]([0,1], repeat=3))
# all 8 binary combos: (0,0,0),(0,0,1),...
# Chaining iterables
list([Link]([1,2], [3,4], [5,6])) # [1,2,3,4,5,6]
9.3 functools
from functools import lru_cache, partial, reduce, wraps, cache
# reduce
reduce(lambda a, b: a * b, [1,2,3,4,5]) # 120
# Course Schedule II
topo_sort(4, [[1,0],[2,0],[3,1],[3,2]]) # [0,1,2,3] or [0,2,1,3]
def count_bits(n):
count = 0
while n:
n &= n - 1 # clears lowest set bit
count += 1
return count
# Power of two
def is_power_of_two(n):
return n > 0 and (n & (n-1)) == 0
🎯 THE GOLDEN RULE: Write brute force first → verify it's correct → identify the bottleneck (usually a
nested loop or repeated work) → eliminate it with the right data structure or algorithm pattern from this
guide.
📖 HOW TO USE THIS REFERENCE
This section is your quick-recall layer. For every concept in the guide, you get:
🎯 Goal
After reading this section you should be able to hear a problem description and immediately think:
'That's a sliding window' or 'That needs a heap' — without looking anything up.
SECTION 1 NOTES — Python Fundamentals
Variables & Types
Dynamic typing — Python figures out the type at runtime — you never declare int x, just x = 5
Everything is an object — Even int and bool are objects with methods. 5 .bit_length() works.
None — The absence of a value. Like null in other languages. Always check with 'is None' not '== None'
Falsy values — False, 0, 0.0, '', [], {}, set(), None all evaluate False in a boolean context
Truthy — Everything else is True. Non-empty string, non-zero number, non-empty list
id() — Returns the memory address of an object. Use to check if two variables point to the same object
Control Flow
range(stop) — Generates 0 to stop-1. range(5) → 0,1,2,3,4. Lazy — doesn't build a list.
range(start,stop,step) — range(10,0,-1) counts down. range(0,20,3) skips by 3.
enumerate(iterable) — Gives you (index, value) pairs. Use instead of range(len(lst)) to avoid off-by-one
bugs.
zip(a,b) — Pairs elements from multiple iterables. Stops at the shortest. Use zip_longest from itertools to
keep all.
break — Exits the innermost loop immediately.
continue — Skips the rest of this iteration and goes to the next one.
pass — Does nothing — a placeholder when syntax requires a block but you have no code yet.
else on loops — for...else / while...else — the else runs only if the loop completed WITHOUT a break.
Comprehensions
List comp [x for x in iterable if condition] — Fastest way to build a list. More readable than
map/filter for simple cases.
Dict comp {k:v for k,v in items} — Build a dict in one line. Great for inverting a dict: {v:k for k,v in
[Link]()}
Set comp {x for x in iterable} — Like list comp but deduplicates automatically.
Generator (x for x in iterable) — Like list comp but LAZY — computes one item at a time. Use for
large data.
Nested comp — [x for row in matrix for x in row] — flattens 2D to 1D. Read left to right like nested for loops.
Tuple
What — Ordered, immutable, hashable. A frozen list.
Hashable — Can be used as dict keys or set elements because it can't change.
Unpacking — a, b, c = (1,2,3) — cleaner than indexing. Works anywhere in assignments.
*rest unpacking — first, *rest = (1,2,3,4) — rest = [2,3,4]. Grab the tail.
When to use — Return multiple values from a function. Fixed data that shouldn't change. Dict keys that are
coordinates.
Dictionary
What — Key-value store. Hash table under the hood. O(1) average for all ops.
get(key, default) — Returns default instead of raising KeyError. Use this over d[key] when key might be
missing.
setdefault(key, default) — Sets the key to default if missing AND returns it. Useful for grouping.
[Link]() — Returns (key,value) pairs. Most common way to iterate a dict.
Counter — Specialized dict for counting. Counter('hello') → {'l':2,'h':1,'e':1,'o':1}
defaultdict — Never raises KeyError. Specify a factory: defaultdict(list) auto-creates [] for missing keys.
Order preserved — Python 3.7+ — dicts remember insertion order.
When to use — Any time you need fast lookup by a meaningful key. Grouping. Frequency counting.
Memoization.
Set
What — Unordered, unique elements. Hash table with no values.
O(1) lookup — The whole point. 'x in my_set' is O(1). 'x in my_list' is O(n).
No duplicates — Adding an existing element does nothing. Great for deduplication.
Set operations — | union & intersection - difference ^ symmetric difference. Very fast.
frozenset — Immutable set. Can be used as dict key or in another set.
When to use — Deduplication. Membership testing. Finding common/unique elements between collections.
Stack
What — Last In First Out (LIFO). Think: undo history, function call stack.
Use a list — append() to push, pop() to pop. Both O(1). That's all you need.
Peek — stack[-1] — look at top without removing.
When to use — Balanced parentheses problems. DFS. Expression evaluation. Monotonic stack patterns.
Queue
What — First In First Out (FIFO). Think: waiting line, BFS frontier.
Use deque — [Link]. appendleft/popleft are O(1). [Link](0) is O(n) — never use that for queues.
BFS pattern — Always. Put start node in queue, process level by level.
When to use — BFS. Level-order tree traversal. Task scheduling. Sliding window (with maxlen).
Linked List
What — Nodes each holding a value and a pointer to the next node.
O(1) insert/delete at head — Just update the pointer. No shifting like arrays.
O(n) access by index — Must walk from head. No random access.
Two-pointer techniques — Fast/slow pointers for cycle detection, finding middle, kth from end.
Dummy head node — Create a ListNode(0) as a fake head to simplify edge cases in manipulation problems.
When to use — LeetCode gives you ListNode. Real world: when inserts/deletes at arbitrary positions are
frequent.
Binary Tree
What — Each node has at most 2 children (left, right).
Inorder L→N→R — For BST gives SORTED order. Key insight for many BST problems.
Preorder N→L→R — Use to serialize/copy a tree. Root comes first.
Postorder L→R→N — Use for deletion, calculating subtree sizes. Children before parent.
BFS level order — Use a deque. Process queue level by level. len(queue) trick snapshots each level's
size.
Height/depth — Recursive: 1 + max(height(left), height(right)). Base: height(None) = 0.
BST property — Left < Node < Right. Enables O(log n) search when balanced.
When to use — Hierarchical data. BST for sorted dynamic data. Heaps built on array representation.
Graph
What — Nodes (vertices) connected by edges. Can be directed or undirected, weighted or unweighted.
Adjacency list — Dict of {node: [neighbors]}. Space O(V+E). Use for sparse graphs (most LeetCode
problems).
BFS — Uses queue. Finds SHORTEST PATH in unweighted graphs. Explores layer by layer.
DFS — Uses stack (or recursion). Explores as deep as possible before backtracking.
visited set — ALWAYS track visited nodes in graph traversal or you'll loop forever on cycles.
When to use — BFS = shortest path, level-based problems. DFS = path existence, cycle detection,
topological sort.
Inheritance
What — Child class gets all methods and attributes of parent. Reuse code, extend behaviour.
super() — Call the parent class's version of a method. Essential in __init__ to run parent setup.
Method overriding — Define same method name in child — child version runs instead of parent's.
MRO (Method Resolution Order) — Python looks for a method: class itself → parents left to right →
object. Use ClassName.__mro__ to see it.
Multiple inheritance — class C(A, B) — C inherits from both. MRO determines which method wins.
isinstance(obj, Class) — Returns True if obj is instance of Class OR any subclass. More flexible than
type().
Encapsulation
What — Hiding internal state, exposing only what's needed. Reduces accidental bugs.
_single_underscore — Convention: 'protected'. Not enforced. Means 'please don't touch this from outside'.
__double_underscore — Name mangling. _ClassName__attr. Harder (not impossible) to access from
outside. True hiding.
@property — Turn a method into an attribute-style access. [Link] not obj.get_radius().
@[Link] — Validate before setting. Raise ValueError if invalid. Keep data consistent.
Abstraction
What — Define WHAT something does, not HOW. Force subclasses to implement details.
ABC + @abstractmethod — from abc import ABC, abstractmethod. Class can't be instantiated until all
abstract methods are implemented.
Interface pattern — Define abstract methods in base class. Each subclass implements them differently.
When to use — Building frameworks, plugin systems, any time you want to enforce a contract.
Dunder (Magic) Methods — The Important Ones
__str__ — str(obj) or print(obj). Human-readable string. This is what users see.
__repr__ — repr(obj). Developer-readable. Should ideally let you recreate the object: 'Point(x=3, y=4)'
__len__ — len(obj). Makes your class work with len().
__eq__ — obj == other. Without this, == checks identity (is), not equality.
__hash__ — hash(obj). Required to use object as dict key or in a set. If you define __eq__ you MUST define
__hash__.
__lt__ __gt__ — Comparison operators. With @total_ordering you only need __eq__ and __lt__, rest are
auto-generated.
__add__ __mul__ etc — Operator overloading. Makes your objects work with + * - etc.
__contains__ — 'x in obj'. Makes your class work with the 'in' operator.
__iter__ + __next__ — Makes your class iterable. Can use in for loops.
__enter__ + __exit__ — Context manager protocol. Makes your class work with 'with' statement.
__getitem__ — obj[key]. Makes your class subscriptable like a list or dict.
Closures
What — Inner function that remembers variables from its enclosing scope, even after outer function returns.
Why — Create functions with 'baked-in' state without using a class.
nonlocal — Use nonlocal x inside inner function to MODIFY (not just read) a variable from outer scope.
Common use — Factory functions: make_adder(5) returns a function that adds 5 to anything.
Decorators
What — A function that wraps another function to add behaviour before/after it runs.
Syntax @decorator — Just syntactic sugar. @timer above def foo is the same as foo = timer(foo).
@[Link](func) — Always put this in your decorator wrapper. Preserves the original function's
name and docstring.
*args **kwargs in wrapper — Always pass *args, **kwargs through so wrapped function still receives its
arguments.
Common uses — Timing, logging, authentication, caching, retry logic, rate limiting.
Stacking decorators — @dec1 @dec2 def foo — applied bottom up: dec1(dec2(foo)). Order matters.
Generators
What — Function that produces values one at a time using yield. Pauses and resumes execution.
Memory efficiency — Generator holds ONE value in memory at a time. List holds ALL. Huge difference for
big data.
yield — Pauses function, sends value out. Next call to next() resumes from that point.
yield from — Delegate to another generator or iterable. Cleaner than looping and re-yielding.
Lazy evaluation — Values computed only when requested. Infinite sequences are possible.
Can only iterate once — Once exhausted, re-iterating gives nothing. Create a new generator if needed.
Recursion
What — Function that calls itself. Solves problems by reducing to smaller versions of the same problem.
Base case — The condition that stops recursion. ALWAYS have one or you get infinite recursion.
Call stack — Each recursive call adds a frame to the stack. Python's default limit is 1000.
[Link]() to raise it.
Think in terms of — 'If I had the answer for n-1, how do I get the answer for n?'
Trust the recursion — Don't trace every call. Assume the recursive call does the right thing, just write the
combination logic.
Memoization / Caching
What — Store results of expensive function calls. Return cached result when same inputs appear again.
@lru_cache(maxsize=None) — The Python built-in. Decorator. maxsize=None means unlimited cache.
@cache (3.9+) — Simpler alias for @lru_cache(maxsize=None). Use this in modern Python.
Manual dict memo — def f(n, memo={}): — but careful, mutable default args are shared across calls.
When to use — Any recursive function where the same inputs recur (overlapping subproblems).
Dynamic Programming
What — Break a problem into overlapping subproblems, solve each once, store results.
Two flavors — Top-down = recursion + memoization. Bottom-up = tabulation (fill a table iteratively).
Memoization (top-down) — Natural — write the recursive solution, add @cache. Easy to reason about.
Tabulation (bottom-up) — Iterative. Fill dp[] from base cases up. Often more space-efficient.
State design — dp[i] means 'answer for subproblem i'. Define what i represents clearly FIRST.
Transition — How do you go from dp[i-1] to dp[i]? This is the recurrence relation.
Space optimization — Often you only need the last 1-2 values. Use variables instead of full array.
When to recognize DP — 'Optimal' + 'count ways' + 'can you achieve X' + overlapping subproblems = try
DP.
Reference Counting
What — Python tracks how many variables point to each object. When count hits 0, object is freed.
del x — Decrements the reference count of x's object. If count → 0, memory is freed.
[Link](x) — Shows reference count. Always at least 2 because the function call itself adds one.
Circular references — A → B → A. Both have count > 0 even after del. Reference counting can't free
these.
Cyclic GC — Python's garbage collector (gc module) detects and frees circular references periodically.
Memory Optimization
__slots__ — Class attribute. Prevents per-instance __dict__. Saves ~200 bytes per instance when you have
millions.
Generator over list — (x for x in data) uses constant memory. [x for x in data] builds full list in RAM.
array module — [Link]('i', data) stores raw C integers. ~4 bytes each vs 28 bytes for Python int object.
[Link]() — Shallow size — size of the container only. Doesn't include nested objects.
tracemalloc — Built-in memory profiler. Start → run code → snapshot → see what allocated the most.
intern strings — [Link]('key') — forces string to be reused from pool. Speeds up string comparison.
NumPy
What — Fast array math. Operations run in C, not Python. Vectorized = no Python loops needed.
ndarray — The core object. Typed, fixed-size, multi-dimensional array.
Broadcasting — Lets you do arr + 5 — applies to every element. Works on arrays of compatible shapes.
Boolean indexing — arr[arr > 5] — returns elements matching condition. Very powerful filter.
axis= — Many ops take axis=0 (along rows, down columns) or axis=1 (along columns, across rows).
@ operator — Matrix multiplication. A @ B. Or [Link](A, B).
When to use — Any math-heavy code. Image processing. ML features. Anything with large numerical arrays.
Pandas
DataFrame — 2D table. Rows and columns. Like a spreadsheet or SQL table in Python.
Series — 1D column. Single column of a DataFrame.
iloc vs loc — iloc = integer index position (like list). loc = label-based index name.
df[condition] — Boolean filtering. df[df['age'] > 30] returns rows where age > 30.
groupby() — Split data into groups, apply function, combine results. SQL GROUP BY equivalent.
apply(func) — Apply any function to each row or column. Most flexible but slowest.
merge() — Combine two DataFrames on a key column. Like SQL JOIN.
fillna() / dropna() — Handle missing values. dropna() removes rows with any NaN. fillna(0) replaces
with 0.
When to use — Data analysis, cleaning, CSV/Excel processing, feature engineering for ML.
Sorting
Python built-in sort — Use sorted() or .sort() in real code. Timsort. O(n log n). Stable. Incredibly
optimized.
Bubble sort — Educational only. O(n²). Never use in production or interviews unless asked.
Merge sort — Divide in half, sort each, merge. O(n log n) guaranteed. O(n) space. Stable.
Quick sort — Pick pivot, partition, recurse. O(n log n) avg, O(n²) worst. O(log n) space. Fastest in practice.
Heap sort — Build heap, pop repeatedly. O(n log n) always. O(1) space. Not stable.
Counting sort — When values are in known small range. O(n + k). Faster than comparison-based sorts.
Searching
Linear search — Check every element. O(n). Use when unsorted or only searching once.
Binary search — REQUIRES SORTED data. Halve search space each step. O(log n). Template: lo/hi/mid.
bisect module — Python's built-in binary search. bisect_left = leftmost position. bisect_right = rightmost.
Binary search on answer — When you can binary search the VALUE space not the array. 'What's the
minimum X such that...'
Two Pointers
What — Two indices moving through data (usually from both ends or at different speeds).
Pair sum in sorted array — Left pointer from start, right from end. Move based on current sum vs
target.
Remove duplicates — Slow pointer = boundary of unique section. Fast pointer scans ahead.
When to use — Sorted arrays. Palindrome check. Container with most water. Three sum.
Sliding Window
What — A contiguous subarray/substring that moves through data. Expand right, shrink left.
Fixed size window — Subtract element leaving, add element entering. O(n) instead of O(n²).
Variable size window — Expand right always, shrink left when window violates constraint.
When to use — Longest/shortest subarray with property. Max/min sum of size k. Substring problems.
BFS vs DFS
BFS (Breadth First Search) — Queue. Level by level. Finds SHORTEST PATH in unweighted graphs.
DFS (Depth First Search) — Stack (or recursion). Goes deep first. Good for path existence, cycle
detection.
BFS space — O(w) where w is max width of graph. Can be large on wide graphs.
DFS space — O(h) where h is depth. Can blow stack on very deep graphs. Use iterative DFS for safety.
Grid BFS — Treat each cell as a node. Neighbors = up/down/left/right. Mark visited immediately when adding
to queue.
Backtracking
What — Try a choice, recurse, undo if it doesn't work. Explore all possibilities.
Template — make choice → recurse → undo choice. The 'undo' is what makes it backtracking.
Pruning — Add conditions to skip invalid paths early. Makes it much faster than brute force.
When to use — Permutations, combinations, subsets, N-Queens, Sudoku, word search on grid.
Context Managers
What — Objects that set up and tear down resources. The 'with' statement guarantees cleanup.
__enter__ — Runs when entering 'with' block. Returns value bound to 'as' variable.
__exit__ — Runs when leaving 'with' block — ALWAYS, even if exception occurs.
@contextmanager — Generator shortcut. Code before yield = enter. Code after yield = exit. Put in try/finally.
Common uses — File handles. DB connections. Locks. Timers. Temporary state changes.
Why — Without it, resources leak if exceptions occur. 'with' guarantees cleanup.
Async / Await
What — Concurrency for I/O-bound tasks. Run other code WHILE waiting for network/disk.
async def — Defines a coroutine. Calling it returns a coroutine object, not the result.
await — Pause this coroutine until the awaited thing completes. Other coroutines can run meanwhile.
[Link]() — Entry point. Run your async main() function from synchronous code.
[Link]() — Run multiple coroutines CONCURRENTLY. Waits for all to finish.
[Link](0) — Yield control to event loop. Let other coroutines run.
NOT parallelism — Only one coroutine runs at a time. The benefit is not blocking during I/O waits.
When to use — Many network requests, file operations, or DB queries happening at once.
Threading vs Multiprocessing
GIL — Global Interpreter Lock. Only one thread executes Python bytecode at a time. True CPU parallelism
blocked.
Threading — Good for I/O-bound tasks (network, file). One thread waits for I/O, others can run.
Multiprocessing — Good for CPU-bound tasks. Each process has its own GIL. True parallelism.
ThreadPoolExecutor — High-level interface. submit() returns a Future. result() blocks until done.
ProcessPoolExecutor — Same interface as ThreadPoolExecutor but uses processes. Slow startup, true
parallelism.
Lock — Prevent race conditions when threads share data. with lock: around critical sections.
Metaclasses
What — The class of a class. type is the default metaclass of all Python classes.
type(name, bases, dict) — Create a class dynamically at runtime without the class keyword.
Custom metaclass — Override __call__ in metaclass to control instance creation.
Singleton pattern — Override __call__ to return the same instance every time.
__new__ vs __init__ — __new__ creates the object (allocation). __init__ initializes it (setup). __new__
runs first.
When to use — Framework code, ORM systems (like Django models), plugin registration, enforcing APIs.
itertools
combinations(it, r) — All r-length combos without repetition. C(n,r). Order doesn't matter.
permutations(it, r) — All r-length arrangements. P(n,r). Order matters.
product(*its, repeat=n) — Cartesian product. All combinations of elements from multiple iterables.
chain(*its) — Flatten multiple iterables into one lazy stream.
groupby(it, key) — Group consecutive elements by key. Sort first if you want all groups together.
accumulate(it, func) — Running accumulation. Default is addition (running sum). Can pass any binary
function.
islice(it, stop) — Lazy slice of an iterator. First n elements without building full list.
functools
@lru_cache — Memoize. Cache return values keyed by arguments. maxsize=None = unlimited.
@cache — Python 3.9+. Same as @lru_cache(maxsize=None). Simpler syntax.
partial(func, *args) — Pre-fill some arguments of a function. Returns new function.
reduce(func, it) — Fold iterable to single value. reduce(lambda a,b: a+b, [1,2,3,4]) = 10
@total_ordering — Define __eq__ and __lt__, get all other comparison methods free.
@wraps(func) — Use inside decorators to preserve the wrapped function's metadata.
heapq
Min-heap — Always. Python has no built-in max-heap. Use negated values for max behavior.
heappush(h, val) — O(log n). Insert and maintain heap property.
heappop(h) — O(log n). Remove and return smallest element.
heapify(lst) — O(n). Convert list to valid heap in-place. Much faster than n pushes.
h[0] — O(1) peek at minimum without removing.
nsmallest / nlargest — [Link](k, lst). Efficient for small k. Uses heap internally.
bisect
bisect_left(arr, x) — Leftmost index to insert x in sorted arr. If x exists, index of x.
bisect_right(arr, x) — Rightmost index to insert x. If x exists, index after last x.
insort(arr, x) — Insert x into sorted arr maintaining sort. O(n) due to list shift but O(log n) search.
Use case — Binary search with O(log n). Count elements < x with bisect_left.