| name | python-patterns |
| description | Pythonic idioms, PEP 8, type hints, and best practices. Use when writing, reviewing, or refactoring Python code. |
| origin | MCC |
Python Development Patterns
Idiomatic Python patterns and best practices for building robust, efficient, and maintainable applications.
When to Activate
- Writing new Python code
- Reviewing Python code
- Refactoring existing Python code
- Designing Python packages/modules
Core Principles
1. Readability Counts
Python prioritizes readability. Code should be obvious and easy to understand.
def get_active_users(users: list[User]) -> list[User]:
"""Return only active users from the provided list."""
return [user for user in users if user.is_active]
def get_active_users(u):
return [x for x in u if x.a]
2. Explicit is Better Than Implicit
Avoid magic; be clear about what your code does.
import logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
import some_module
some_module.setup()
3. EAFP - Easier to Ask Forgiveness Than Permission
Python prefers exception handling over checking conditions.
def get_value(dictionary: dict, key: str) -> Any:
try:
return dictionary[key]
except KeyError:
return default_value
def get_value(dictionary: dict, key: str) -> Any:
if key in dictionary:
return dictionary[key]
else:
return default_value
Type Hints
Basic Type Annotations
from typing import Optional, List, Dict, Any
def process_user(
user_id: str,
data: Dict[str, Any],
active: bool = True
) -> Optional[User]:
"""Process a user and return the updated User or None."""
if not active:
return None
return User(user_id, data)
Modern Type Hints (Python 3.9+)
def process_items(items: list[str]) -> dict[str, int]:
return {item: len(item) for item in items}
Type Aliases, TypeVar, and Protocols
from typing import TypeVar, Union, Protocol
JSON = Union[dict[str, Any], list[Any], str, int, float, bool, None]
T = TypeVar('T')
def first(items: list[T]) -> T | None:
return items[0] if items else None
class Renderable(Protocol):
def render(self) -> str: ...
def render_all(items: list[Renderable]) -> str:
return "\n".join(item.render() for item in items)
Error Handling Patterns
Specific Exception Handling
def load_config(path: str) -> Config:
try:
with open(path) as f:
return Config.from_json(f.read())
except FileNotFoundError as e:
raise ConfigError(f"Config file not found: {path}") from e
except json.JSONDecodeError as e:
raise ConfigError(f"Invalid JSON in config: {path}") from e
Custom Exception Hierarchy
class AppError(Exception):
"""Base exception for all application errors."""
pass
class ValidationError(AppError):
"""Raised when input validation fails."""
pass
class NotFoundError(AppError):
"""Raised when a requested resource is not found."""
pass
Quick Reference: Python Idioms
| Idiom | Description |
|---|
| EAFP | Easier to Ask Forgiveness than Permission |
| Context managers | Use with for resource management |
| List comprehensions | For simple transformations |
| Generators | For lazy evaluation and large datasets |
| Type hints | Annotate function signatures |
| Dataclasses | For data containers with auto-generated methods |
__slots__ | For memory optimization |
| f-strings | For string formatting (Python 3.6+) |
pathlib.Path | For path operations (Python 3.4+) |
enumerate | For index-element pairs in loops |
Anti-Patterns to Avoid
def append_to(item, items=[]):
items.append(item)
return items
def append_to(item, items=None):
if items is None:
items = []
items.append(item)
return items
if type(obj) == list:
process(obj)
if isinstance(obj, list):
process(obj)
if value == None:
process()
if value is None:
process()
from os.path import *
from os.path import join, exists
try:
risky_operation()
except:
pass
try:
risky_operation()
except SpecificError as e:
logger.error(f"Operation failed: {e}")
Remember: Python code should be readable, explicit, and follow the principle of least surprise. When in doubt, prioritize clarity over cleverness.
Reference Files
- detailed-examples.md -- Context managers, comprehensions, generators, data classes, decorators, concurrency, package organization, memory/performance, and tooling configuration