Table of contents
Open Table of contents
Overview
Python’s power lies in its consistent data model and composable abstractions. This reference covers the core concepts you’ll encounter throughout the kata exercises.
The Data Model (Dunder Methods)
Python objects communicate through special (“dunder”) methods. Implementing them makes your objects work with built-in operations.
class Vector:
def __init__(self, x: float, y: float) -> None:
self.x = x
self.y = y
def __repr__(self) -> str:
return f"Vector({self.x}, {self.y})"
def __add__(self, other: "Vector") -> "Vector":
return Vector(self.x + other.x, self.y + other.y)
def __eq__(self, other: object) -> bool:
if not isinstance(other, Vector):
return NotImplemented
return self.x == other.x and self.y == other.y
def __hash__(self) -> int:
return hash((self.x, self.y))
def __abs__(self) -> float:
return (self.x ** 2 + self.y ** 2) ** 0.5
Key Dunder Methods for Data Structures
| Method | Triggered By | Use Case |
|---|---|---|
__len__ | len(obj) | Collection size |
__getitem__ | obj[key] | Index/key access |
__setitem__ | obj[key] = val | Index/key assignment |
__contains__ | x in obj | Membership test |
__iter__ | for x in obj | Iteration |
__next__ | next(obj) | Iterator protocol |
__eq__, __lt__, etc. | ==, < | Comparison |
__hash__ | hash(obj) | Dict keys, set members |
Decorators
Functions that wrap other functions or classes:
import functools
import time
def timer(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
elapsed = time.perf_counter() - start
print(f"{func.__name__} took {elapsed:.4f}s")
return result
return wrapper
@timer
def slow_function():
time.sleep(1)
Decorators with Arguments
def retry(max_attempts: int = 3):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(max_attempts):
try:
return func(*args, **kwargs)
except Exception:
if attempt == max_attempts - 1:
raise
return wrapper
return decorator
@retry(max_attempts=5)
def fetch_data(url: str) -> dict:
...
Class Decorators
from dataclasses import dataclass
@dataclass(frozen=True)
class Point:
x: float
y: float
Generators and Iterators
The Iterator Protocol
Any object implementing __iter__ and __next__:
class Range:
def __init__(self, start: int, stop: int) -> None:
self.start = start
self.stop = stop
def __iter__(self):
current = self.start
while current < self.stop:
yield current
current += 1
Generator Functions
Lazy evaluation with yield:
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
# Take first 10
from itertools import islice
first_10 = list(islice(fibonacci(), 10))
Generator Expressions
squares = (x ** 2 for x in range(1_000_000)) # lazy, no list in memory
total = sum(x ** 2 for x in range(1_000_000)) # direct to sum
Context Managers
Manage setup/teardown with with statements:
from contextlib import contextmanager
@contextmanager
def database_transaction(db):
tx = db.begin()
try:
yield tx
tx.commit()
except Exception:
tx.rollback()
raise
with database_transaction(db) as tx:
tx.execute("INSERT INTO ...")
Class-Based Context Manager
class Timer:
def __enter__(self):
self.start = time.perf_counter()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.elapsed = time.perf_counter() - self.start
return False # don't suppress exceptions
with Timer() as t:
do_work()
print(f"Elapsed: {t.elapsed:.4f}s")
Type Hints
Basic Annotations
from collections.abc import Sequence
def process_items(items: Sequence[str], limit: int = 10) -> list[str]:
return [item.upper() for item in items[:limit]]
Generics (Python 3.12+)
class Stack[T]:
def __init__(self) -> None:
self._items: list[T] = []
def push(self, item: T) -> None:
self._items.append(item)
def pop(self) -> T:
if not self._items:
raise IndexError("pop from empty stack")
return self._items.pop()
Protocols (Structural Typing)
Duck typing with type safety:
from typing import Protocol
class Comparable(Protocol):
def __lt__(self, other: "Comparable") -> bool: ...
def find_min[T: Comparable](items: list[T]) -> T:
return min(items)
TypedDict
from typing import TypedDict
class UserData(TypedDict):
name: str
email: str
age: int
The collections Module
defaultdict
from collections import defaultdict
graph: defaultdict[str, list[str]] = defaultdict(list)
graph["a"].append("b")
graph["a"].append("c")
# No KeyError -- missing keys get default value
deque
Double-ended queue with O(1) operations at both ends:
from collections import deque
d: deque[int] = deque([1, 2, 3])
d.appendleft(0) # O(1)
d.pop() # O(1)
d.popleft() # O(1)
# Bounded deque (sliding window)
recent: deque[str] = deque(maxlen=5)
Counter
from collections import Counter
words = ["apple", "banana", "apple", "cherry", "banana", "apple"]
counts = Counter(words)
print(counts.most_common(2)) # [('apple', 3), ('banana', 2)]
namedtuple and NamedTuple
from typing import NamedTuple
class Point(NamedTuple):
x: float
y: float
p = Point(1.0, 2.0)
x, y = p # unpacking works
Comprehensions
List, Dict, Set
# List
squares = [x ** 2 for x in range(10) if x % 2 == 0]
# Dict
word_lengths = {word: len(word) for word in ["hello", "world"]}
# Set
unique_chars = {c for word in words for c in word}
Nested Comprehensions
# Flatten a matrix
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [x for row in matrix for x in row]
dataclasses
from dataclasses import dataclass, field
@dataclass
class TreeNode:
value: int
children: list["TreeNode"] = field(default_factory=list)
def add_child(self, child: "TreeNode") -> None:
self.children.append(child)
Frozen Dataclasses (Immutable)
@dataclass(frozen=True)
class Coordinate:
lat: float
lon: float
functools Highlights
from functools import lru_cache, reduce, partial
# Memoization
@lru_cache(maxsize=128)
def fibonacci(n: int) -> int:
if n < 2:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
# Reduce
total = reduce(lambda a, b: a + b, [1, 2, 3, 4]) # 10
# Partial application
from operator import mul
double = partial(mul, 2)
Key Takeaways
- Dunder methods — make your objects work with Python’s built-in operations
- Generators — lazy evaluation is idiomatic Python
- Protocols — structural typing (duck typing with type safety)
collections—defaultdict,deque,Countersolve common problems efficiently- Dataclasses — the default choice for data containers
Related Katas
- Kata: Linked List —
__iter__,__len__,__repr__ - Kata: Graph Traversal —
defaultdictfor adjacency lists - Kata: Stack and Queue —
dequefor efficient queues - Kata: Decorator Pattern — Python’s native decorator system
- Kata: Binary Search —
bisectmodule - Kata: Merge Sort — comprehensions and slicing