mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-03-30 13:43:26 +08:00
429 lines
9.3 KiB
Markdown
429 lines
9.3 KiB
Markdown
---
|
|
name: python-patterns
|
|
description: >
|
|
Python-specific design patterns and best practices including protocols,
|
|
dataclasses, context managers, decorators, async/await, type hints, and
|
|
package organization. Use when working with Python code to apply Pythonic
|
|
patterns.
|
|
metadata:
|
|
origin: ECC
|
|
globs: ["**/*.py", "**/*.pyi"]
|
|
---
|
|
|
|
# Python Patterns
|
|
|
|
> This skill provides comprehensive Python patterns extending common design principles with Python-specific idioms.
|
|
|
|
## Protocol (Duck Typing)
|
|
|
|
Use `Protocol` for structural subtyping (duck typing with type hints):
|
|
|
|
```python
|
|
from typing import Protocol
|
|
|
|
class Repository(Protocol):
|
|
def find_by_id(self, id: str) -> dict | None: ...
|
|
def save(self, entity: dict) -> dict: ...
|
|
|
|
# Any class with these methods satisfies the protocol
|
|
class UserRepository:
|
|
def find_by_id(self, id: str) -> dict | None:
|
|
# implementation
|
|
pass
|
|
|
|
def save(self, entity: dict) -> dict:
|
|
# implementation
|
|
pass
|
|
|
|
def process_entity(repo: Repository, id: str) -> None:
|
|
entity = repo.find_by_id(id)
|
|
# ... process
|
|
```
|
|
|
|
**Benefits:**
|
|
- Type safety without inheritance
|
|
- Flexible, loosely coupled code
|
|
- Easy testing and mocking
|
|
|
|
## Dataclasses as DTOs
|
|
|
|
Use `dataclass` for data transfer objects and value objects:
|
|
|
|
```python
|
|
from dataclasses import dataclass, field
|
|
from typing import Optional
|
|
|
|
@dataclass
|
|
class CreateUserRequest:
|
|
name: str
|
|
email: str
|
|
age: Optional[int] = None
|
|
tags: list[str] = field(default_factory=list)
|
|
|
|
@dataclass(frozen=True)
|
|
class User:
|
|
"""Immutable user entity"""
|
|
id: str
|
|
name: str
|
|
email: str
|
|
```
|
|
|
|
**Features:**
|
|
- Auto-generated `__init__`, `__repr__`, `__eq__`
|
|
- `frozen=True` for immutability
|
|
- `field()` for complex defaults
|
|
- Type hints for validation
|
|
|
|
## Context Managers
|
|
|
|
Use context managers (`with` statement) for resource management:
|
|
|
|
```python
|
|
from contextlib import contextmanager
|
|
from typing import Generator
|
|
|
|
@contextmanager
|
|
def database_transaction(db) -> Generator[None, None, None]:
|
|
"""Context manager for database transactions"""
|
|
try:
|
|
yield
|
|
db.commit()
|
|
except Exception:
|
|
db.rollback()
|
|
raise
|
|
|
|
# Usage
|
|
with database_transaction(db):
|
|
db.execute("INSERT INTO users ...")
|
|
```
|
|
|
|
**Class-based context manager:**
|
|
|
|
```python
|
|
class FileProcessor:
|
|
def __init__(self, filename: str):
|
|
self.filename = filename
|
|
self.file = None
|
|
|
|
def __enter__(self):
|
|
self.file = open(self.filename, 'r')
|
|
return self.file
|
|
|
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
if self.file:
|
|
self.file.close()
|
|
return False # Don't suppress exceptions
|
|
```
|
|
|
|
## Generators
|
|
|
|
Use generators for lazy evaluation and memory-efficient iteration:
|
|
|
|
```python
|
|
def read_large_file(filename: str):
|
|
"""Generator for reading large files line by line"""
|
|
with open(filename, 'r') as f:
|
|
for line in f:
|
|
yield line.strip()
|
|
|
|
# Memory-efficient processing
|
|
for line in read_large_file('huge.txt'):
|
|
process(line)
|
|
```
|
|
|
|
**Generator expressions:**
|
|
|
|
```python
|
|
# Instead of list comprehension
|
|
squares = (x**2 for x in range(1000000)) # Lazy evaluation
|
|
|
|
# Pipeline pattern
|
|
numbers = (x for x in range(100))
|
|
evens = (x for x in numbers if x % 2 == 0)
|
|
squares = (x**2 for x in evens)
|
|
```
|
|
|
|
## Decorators
|
|
|
|
### Function Decorators
|
|
|
|
```python
|
|
from functools import wraps
|
|
import time
|
|
|
|
def timing(func):
|
|
"""Decorator to measure execution time"""
|
|
@wraps(func)
|
|
def wrapper(*args, **kwargs):
|
|
start = time.time()
|
|
result = func(*args, **kwargs)
|
|
end = time.time()
|
|
print(f"{func.__name__} took {end - start:.2f}s")
|
|
return result
|
|
return wrapper
|
|
|
|
@timing
|
|
def slow_function():
|
|
time.sleep(1)
|
|
```
|
|
|
|
### Class Decorators
|
|
|
|
```python
|
|
def singleton(cls):
|
|
"""Decorator to make a class a singleton"""
|
|
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 Config:
|
|
pass
|
|
```
|
|
|
|
## Async/Await
|
|
|
|
### Async Functions
|
|
|
|
```python
|
|
import asyncio
|
|
from typing import List
|
|
|
|
async def fetch_user(user_id: str) -> dict:
|
|
"""Async function for I/O-bound operations"""
|
|
await asyncio.sleep(0.1) # Simulate network call
|
|
return {"id": user_id, "name": "Alice"}
|
|
|
|
async def fetch_all_users(user_ids: List[str]) -> List[dict]:
|
|
"""Concurrent execution with asyncio.gather"""
|
|
tasks = [fetch_user(uid) for uid in user_ids]
|
|
return await asyncio.gather(*tasks)
|
|
|
|
# Run async code
|
|
asyncio.run(fetch_all_users(["1", "2", "3"]))
|
|
```
|
|
|
|
### Async Context Managers
|
|
|
|
```python
|
|
class AsyncDatabase:
|
|
async def __aenter__(self):
|
|
await self.connect()
|
|
return self
|
|
|
|
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
await self.disconnect()
|
|
|
|
async with AsyncDatabase() as db:
|
|
await db.query("SELECT * FROM users")
|
|
```
|
|
|
|
## Type Hints
|
|
|
|
### Advanced Type Hints
|
|
|
|
```python
|
|
from typing import TypeVar, Generic, Callable, ParamSpec, Concatenate
|
|
|
|
T = TypeVar('T')
|
|
P = ParamSpec('P')
|
|
|
|
class Repository(Generic[T]):
|
|
"""Generic repository pattern"""
|
|
def __init__(self, entity_type: type[T]):
|
|
self.entity_type = entity_type
|
|
|
|
def find_by_id(self, id: str) -> T | None:
|
|
# implementation
|
|
pass
|
|
|
|
# Type-safe decorator
|
|
def log_call(func: Callable[P, T]) -> Callable[P, T]:
|
|
@wraps(func)
|
|
def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
|
|
print(f"Calling {func.__name__}")
|
|
return func(*args, **kwargs)
|
|
return wrapper
|
|
```
|
|
|
|
### Union Types (Python 3.10+)
|
|
|
|
```python
|
|
def process(value: str | int | None) -> str:
|
|
match value:
|
|
case str():
|
|
return value.upper()
|
|
case int():
|
|
return str(value)
|
|
case None:
|
|
return "empty"
|
|
```
|
|
|
|
## Dependency Injection
|
|
|
|
### Constructor Injection
|
|
|
|
```python
|
|
class UserService:
|
|
def __init__(
|
|
self,
|
|
repository: Repository,
|
|
logger: Logger,
|
|
cache: Cache | None = None
|
|
):
|
|
self.repository = repository
|
|
self.logger = logger
|
|
self.cache = cache
|
|
|
|
def get_user(self, user_id: str) -> User | None:
|
|
if self.cache:
|
|
cached = self.cache.get(user_id)
|
|
if cached:
|
|
return cached
|
|
|
|
user = self.repository.find_by_id(user_id)
|
|
if user and self.cache:
|
|
self.cache.set(user_id, user)
|
|
|
|
return user
|
|
```
|
|
|
|
## Package Organization
|
|
|
|
### Project Structure
|
|
|
|
```
|
|
project/
|
|
├── src/
|
|
│ └── mypackage/
|
|
│ ├── __init__.py
|
|
│ ├── domain/ # Business logic
|
|
│ │ ├── __init__.py
|
|
│ │ └── models.py
|
|
│ ├── services/ # Application services
|
|
│ │ ├── __init__.py
|
|
│ │ └── user_service.py
|
|
│ └── infrastructure/ # External dependencies
|
|
│ ├── __init__.py
|
|
│ └── database.py
|
|
├── tests/
|
|
│ ├── unit/
|
|
│ └── integration/
|
|
├── pyproject.toml
|
|
└── README.md
|
|
```
|
|
|
|
### Module Exports
|
|
|
|
```python
|
|
# __init__.py
|
|
from .models import User, Product
|
|
from .services import UserService
|
|
|
|
__all__ = ['User', 'Product', 'UserService']
|
|
```
|
|
|
|
## Error Handling
|
|
|
|
### Custom Exceptions
|
|
|
|
```python
|
|
class DomainError(Exception):
|
|
"""Base exception for domain errors"""
|
|
pass
|
|
|
|
class UserNotFoundError(DomainError):
|
|
"""Raised when user is not found"""
|
|
def __init__(self, user_id: str):
|
|
self.user_id = user_id
|
|
super().__init__(f"User {user_id} not found")
|
|
|
|
class ValidationError(DomainError):
|
|
"""Raised when validation fails"""
|
|
def __init__(self, field: str, message: str):
|
|
self.field = field
|
|
self.message = message
|
|
super().__init__(f"{field}: {message}")
|
|
```
|
|
|
|
### Exception Groups (Python 3.11+)
|
|
|
|
```python
|
|
try:
|
|
# Multiple operations
|
|
pass
|
|
except* ValueError as eg:
|
|
# Handle all ValueError instances
|
|
for exc in eg.exceptions:
|
|
print(f"ValueError: {exc}")
|
|
except* TypeError as eg:
|
|
# Handle all TypeError instances
|
|
for exc in eg.exceptions:
|
|
print(f"TypeError: {exc}")
|
|
```
|
|
|
|
## Property Decorators
|
|
|
|
```python
|
|
class User:
|
|
def __init__(self, name: str):
|
|
self._name = name
|
|
self._email = None
|
|
|
|
@property
|
|
def name(self) -> str:
|
|
"""Read-only property"""
|
|
return self._name
|
|
|
|
@property
|
|
def email(self) -> str | None:
|
|
return self._email
|
|
|
|
@email.setter
|
|
def email(self, value: str) -> None:
|
|
if '@' not in value:
|
|
raise ValueError("Invalid email")
|
|
self._email = value
|
|
```
|
|
|
|
## Functional Programming
|
|
|
|
### Higher-Order Functions
|
|
|
|
```python
|
|
from functools import reduce
|
|
from typing import Callable, TypeVar
|
|
|
|
T = TypeVar('T')
|
|
U = TypeVar('U')
|
|
|
|
def pipe(*functions: Callable) -> Callable:
|
|
"""Compose functions left to right"""
|
|
def inner(arg):
|
|
return reduce(lambda x, f: f(x), functions, arg)
|
|
return inner
|
|
|
|
# Usage
|
|
process = pipe(
|
|
str.strip,
|
|
str.lower,
|
|
lambda s: s.replace(' ', '_')
|
|
)
|
|
result = process(" Hello World ") # "hello_world"
|
|
```
|
|
|
|
## When to Use This Skill
|
|
|
|
- Designing Python APIs and packages
|
|
- Implementing async/concurrent systems
|
|
- Structuring Python projects
|
|
- Writing Pythonic code
|
|
- Refactoring Python codebases
|
|
- Type-safe Python development
|