Python Patterns Skill
Write Pythonic, clean, and maintainable Python code
🎯 PRINCIPLES
1. The Zen of Python
python
import this # Beautiful is better than ugly. # Explicit is better than implicit. # Simple is better than complex. # Readability counts. # There should be one-- and preferably only one --obvious way to do it.
2. PEP 8 Quick Reference
- •4 spaces for indentation
- •Max line length: 88-120 characters
- •snake_case for functions/variables
- •PascalCase for classes
- •UPPER_SNAKE for constants
- •Two blank lines between top-level definitions
3. Type Hints (PEP 484)
python
from typing import Optional, Union, TypeVar, Generic
from collections.abc import Callable, Iterable
def greet(name: str) -> str:
return f"Hello, {name}"
def process(items: list[int]) -> dict[str, int]:
return {"sum": sum(items), "count": len(items)}
# Optional = Union[X, None]
def find_user(id: int) -> Optional[User]:
...
# TypeVar for generics
T = TypeVar('T')
def first(items: list[T]) -> T | None:
return items[0] if items else None
📘 COMPREHENSIONS
List Comprehension
python
# ✅ Good - Simple transformation
squares = [x**2 for x in range(10)]
names = [user.name for user in users]
# ✅ Good - With condition
evens = [x for x in numbers if x % 2 == 0]
adults = [u for u in users if u.age >= 18]
# ✅ Good - Nested (flatten)
flat = [item for sublist in nested for item in sublist]
# ❌ Bad - Too complex
# Use regular loop instead
result = [
transform(x) for x in items
if condition(x)
for y in x.children
if other_condition(y)
]
Dict Comprehension
python
# Create dict from list
user_by_id = {u.id: u for u in users}
# Swap keys and values
inverted = {v: k for k, v in original.items()}
# Filter dict
filtered = {k: v for k, v in data.items() if v > 0}
Set Comprehension
python
# Unique values
unique_names = {user.name.lower() for user in users}
🎨 DECORATORS
Basic Decorator
python
from functools import wraps
from typing import Callable, TypeVar, ParamSpec
P = ParamSpec('P')
R = TypeVar('R')
def log_calls(func: Callable[P, R]) -> Callable[P, R]:
@wraps(func) # Preserves function metadata
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
print(f"Calling {func.__name__}")
result = func(*args, **kwargs)
print(f"Finished {func.__name__}")
return result
return wrapper
@log_calls
def process_data(data: list[int]) -> int:
return sum(data)
Decorator with Arguments
python
def retry(max_attempts: int = 3, delay: float = 1.0):
def decorator(func: Callable[P, R]) -> Callable[P, R]:
@wraps(func)
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
for attempt in range(max_attempts):
try:
return func(*args, **kwargs)
except Exception as e:
if attempt == max_attempts - 1:
raise
time.sleep(delay)
raise RuntimeError("Should not reach here")
return wrapper
return decorator
@retry(max_attempts=5, delay=0.5)
def fetch_data(url: str) -> dict:
...
Class Decorator
python
def singleton(cls):
instances = {}
@wraps(cls)
def get_instance(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return get_instance
@singleton
class DatabaseConnection:
...
📦 DATACLASSES
Basic Dataclass
python
from dataclasses import dataclass, field
from datetime import datetime
@dataclass
class User:
id: int
name: str
email: str
created_at: datetime = field(default_factory=datetime.now)
tags: list[str] = field(default_factory=list)
def __post_init__(self):
self.email = self.email.lower()
Frozen (Immutable)
python
@dataclass(frozen=True)
class Point:
x: float
y: float
def distance_to(self, other: 'Point') -> float:
return ((self.x - other.x)**2 + (self.y - other.y)**2)**0.5
With Slots (Memory Efficient)
python
@dataclass(slots=True)
class LargeDataset:
values: list[float]
labels: list[str]
Field Options
python
@dataclass
class Config:
# Required field
name: str
# Default value
debug: bool = False
# Computed default
timestamp: float = field(default_factory=time.time)
# Exclude from repr
secret: str = field(repr=False)
# Exclude from comparison
cache: dict = field(compare=False, default_factory=dict)
🔄 GENERATORS
Generator Function
python
def read_large_file(filepath: str):
"""Memory-efficient file reading."""
with open(filepath) as f:
for line in f:
yield line.strip()
# Usage
for line in read_large_file("huge.txt"):
process(line)
Generator Expression
python
# Lazy evaluation - doesn't create list in memory sum_squares = sum(x**2 for x in range(1_000_000)) # Process large data lazily large_data = (process(x) for x in huge_iterator)
Yield From
python
def flatten(nested: list[list[int]]) -> Iterator[int]:
for sublist in nested:
yield from sublist
# Equivalent to:
# for item in sublist:
# yield item
📋 CONTEXT MANAGERS
Using contextlib
python
from contextlib import contextmanager, asynccontextmanager
@contextmanager
def timer(name: str):
start = time.perf_counter()
try:
yield
finally:
elapsed = time.perf_counter() - start
print(f"{name} took {elapsed:.3f}s")
with timer("Processing"):
do_heavy_work()
Class-based Context Manager
python
class DatabaseTransaction:
def __init__(self, connection):
self.connection = connection
def __enter__(self):
self.connection.begin()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type is None:
self.connection.commit()
else:
self.connection.rollback()
return False # Don't suppress exceptions
🎯 PATTERN MATCHING (3.10+)
python
def process_command(command: dict) -> str:
match command:
case {"action": "create", "name": name}:
return f"Creating {name}"
case {"action": "delete", "id": int() as id}:
return f"Deleting item {id}"
case {"action": "update", "id": id, **rest}:
return f"Updating {id} with {rest}"
case {"action": action}:
return f"Unknown action: {action}"
case _:
return "Invalid command"
# With types
def handle_response(response: tuple) -> None:
match response:
case (200, body):
print(f"Success: {body}")
case (404, _):
print("Not found")
case (code, _) if code >= 500:
print("Server error")
⚠️ ANTI-PATTERNS
❌ Mutable Default Arguments
python
# ❌ BAD - Shared list between calls!
def append_to(item, lst=[]):
lst.append(item)
return lst
# ✅ GOOD
def append_to(item, lst=None):
if lst is None:
lst = []
lst.append(item)
return lst
❌ Bare Except
python
# ❌ BAD - Catches everything including KeyboardInterrupt
try:
risky_operation()
except:
pass
# ✅ GOOD - Be specific
try:
risky_operation()
except (ValueError, TypeError) as e:
logger.error(f"Operation failed: {e}")
❌ Using type() for Type Checking
python
# ❌ BAD - Doesn't handle inheritance
if type(obj) == SomeClass:
...
# ✅ GOOD
if isinstance(obj, SomeClass):
...
❌ String Concatenation in Loops
python
# ❌ BAD - O(n²) complexity
result = ""
for item in items:
result += str(item)
# ✅ GOOD - O(n) complexity
result = "".join(str(item) for item in items)
🔧 MODERN PYTHON FEATURES
Walrus Operator (:=) - 3.8+
python
# Assign and use in one expression
if (n := len(items)) > 10:
print(f"Too many items: {n}")
# In comprehensions
valid = [y for x in data if (y := validate(x)) is not None]
# In while loops
while (line := file.readline()):
process(line)
Union Types (|) - 3.10+
python
# Old style
def process(value: Union[int, str]) -> Optional[Result]:
...
# New style (3.10+)
def process(value: int | str) -> Result | None:
...
F-strings Debug (=) - 3.8+
python
x = 10
y = 20
print(f"{x=}, {y=}") # Output: x=10, y=20
print(f"{x + y = }") # Output: x + y = 30
📎 QUICK REFERENCE
| Pattern | When to Use |
|---|---|
| List Comprehension | Transform/filter sequences |
| Dict Comprehension | Build dicts from iterables |
| Generator | Process large data lazily |
| Decorator | Cross-cutting concerns (logging, timing) |
| Dataclass | Data containers with methods |
| Context Manager | Resource management (files, locks) |
| Pattern Matching | Complex conditional logic |
🔗 RELATED SKILLS
- •async-python - Async/await patterns