<< All versions
Skill v1.0.1
currentLLM-judged scan100/100affaan-m/everything-claude-code/python-patterns
1 files
──Details
PublishedMay 15, 2026 at 07:51 AM
Content Hashsha256:df0c0abbbd0e6a01...
Git SHAf04702bdac13
Bump Typepatch
──Files
Files (1 file, 16.4 KB)
SKILL.md16.4 KBactive
SKILL.md · 752 lines · 16.4 KB
version: "1.0.1" name: python-patterns description: Pythonic idioms, PEP 8 standards, type hints, and best practices for building robust, efficient, and maintainable Python applications. origin: ECC
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.
python
# Good: Clear and readabledef 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]# Bad: Clever but confusingdef 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.
python
# Good: Explicit configurationimport logginglogging.basicConfig(level=logging.INFO,format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')# Bad: Hidden side effectsimport some_modulesome_module.setup() # What does this do?
3. EAFP - Easier to Ask Forgiveness Than Permission
Python prefers exception handling over checking conditions.
python
# Good: EAFP styledef get_value(dictionary: dict, key: str) -> Any:try:return dictionary[key]except KeyError:return default_value# Bad: LBYL (Look Before You Leap) styledef get_value(dictionary: dict, key: str) -> Any:if key in dictionary:return dictionary[key]else:return default_value
Type Hints
Basic Type Annotations
python
from typing import Optional, List, Dict, Anydef 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 Nonereturn User(user_id, data)
Modern Type Hints (Python 3.9+)
python
# Python 3.9+ - Use built-in typesdef process_items(items: list[str]) -> dict[str, int]:return {item: len(item) for item in items}# Python 3.8 and earlier - Use typing modulefrom typing import List, Dictdef process_items(items: List[str]) -> Dict[str, int]:return {item: len(item) for item in items}
Type Aliases and TypeVar
python
from typing import TypeVar, Union# Type alias for complex typesJSON = Union[dict[str, Any], list[Any], str, int, float, bool, None]def parse_json(data: str) -> JSON:return json.loads(data)# Generic typesT = TypeVar('T')def first(items: list[T]) -> T | None:"""Return the first item or None if list is empty."""return items[0] if items else None
Protocol-Based Duck Typing
python
from typing import Protocolclass Renderable(Protocol):def render(self) -> str:"""Render the object to a string."""def render_all(items: list[Renderable]) -> str:"""Render all items that implement the Renderable protocol."""return "\n".join(item.render() for item in items)
Error Handling Patterns
Specific Exception Handling
python
# Good: Catch specific exceptionsdef 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 eexcept json.JSONDecodeError as e:raise ConfigError(f"Invalid JSON in config: {path}") from e# Bad: Bare exceptdef load_config(path: str) -> Config:try:with open(path) as f:return Config.from_json(f.read())except:return None # Silent failure!
Exception Chaining
python
def process_data(data: str) -> Result:try:parsed = json.loads(data)except json.JSONDecodeError as e:# Chain exceptions to preserve the tracebackraise ValueError(f"Failed to parse data: {data}") from e
Custom Exception Hierarchy
python
class AppError(Exception):"""Base exception for all application errors."""passclass ValidationError(AppError):"""Raised when input validation fails."""passclass NotFoundError(AppError):"""Raised when a requested resource is not found."""pass# Usagedef get_user(user_id: str) -> User:user = db.find_user(user_id)if not user:raise NotFoundError(f"User not found: {user_id}")return user
Context Managers
Resource Management
python
# Good: Using context managersdef process_file(path: str) -> str:with open(path, 'r') as f:return f.read()# Bad: Manual resource managementdef process_file(path: str) -> str:f = open(path, 'r')try:return f.read()finally:f.close()
Custom Context Managers
python
from contextlib import contextmanager@contextmanagerdef timer(name: str):"""Context manager to time a block of code."""start = time.perf_counter()yieldelapsed = time.perf_counter() - startprint(f"{name} took {elapsed:.4f} seconds")# Usagewith timer("data processing"):process_large_dataset()
Context Manager Classes
python
class DatabaseTransaction:def __init__(self, connection):self.connection = connectiondef __enter__(self):self.connection.begin_transaction()return selfdef __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# Usagewith DatabaseTransaction(conn):user = conn.create_user(user_data)conn.create_profile(user.id, profile_data)
Comprehensions and Generators
List Comprehensions
python
# Good: List comprehension for simple transformationsnames = [user.name for user in users if user.is_active]# Bad: Manual loopnames = []for user in users:if user.is_active:names.append(user.name)# Complex comprehensions should be expanded# Bad: Too complexresult = [x * 2 for x in items if x > 0 if x % 2 == 0]# Good: Use a generator functiondef filter_and_transform(items: Iterable[int]) -> list[int]:result = []for x in items:if x > 0 and x % 2 == 0:result.append(x * 2)return result
Generator Expressions
python
# Good: Generator for lazy evaluationtotal = sum(x * x for x in range(1_000_000))# Bad: Creates large intermediate listtotal = sum([x * x for x in range(1_000_000)])
Generator Functions
python
def read_large_file(path: str) -> Iterator[str]:"""Read a large file line by line."""with open(path) as f:for line in f:yield line.strip()# Usagefor line in read_large_file("huge.txt"):process(line)
Data Classes and Named Tuples
Data Classes
python
from dataclasses import dataclass, fieldfrom datetime import datetime@dataclassclass User:"""User entity with automatic __init__, __repr__, and __eq__."""id: strname: stremail: strcreated_at: datetime = field(default_factory=datetime.now)is_active: bool = True# Usageuser = User(id="123",name="Alice",email="alice@example.com")
Data Classes with Validation
python
@dataclassclass User:email: strage: intdef __post_init__(self):# Validate email formatif "@" not in self.email:raise ValueError(f"Invalid email: {self.email}")# Validate age rangeif self.age < 0 or self.age > 150:raise ValueError(f"Invalid age: {self.age}")
Named Tuples
python
from typing import NamedTupleclass Point(NamedTuple):"""Immutable 2D point."""x: floaty: floatdef distance(self, other: 'Point') -> float:return ((self.x - other.x) ** 2 + (self.y - other.y) ** 2) ** 0.5# Usagep1 = Point(0, 0)p2 = Point(3, 4)print(p1.distance(p2)) # 5.0
Decorators
Function Decorators
python
import functoolsimport timedef timer(func: Callable) -> Callable:"""Decorator to time function execution."""@functools.wraps(func)def wrapper(*args, **kwargs):start = time.perf_counter()result = func(*args, **kwargs)elapsed = time.perf_counter() - startprint(f"{func.__name__} took {elapsed:.4f}s")return resultreturn wrapper@timerdef slow_function():time.sleep(1)# slow_function() prints: slow_function took 1.0012s
Parameterized Decorators
python
def repeat(times: int):"""Decorator to repeat a function multiple times."""def decorator(func: Callable) -> Callable:@functools.wraps(func)def wrapper(*args, **kwargs):results = []for _ in range(times):results.append(func(*args, **kwargs))return resultsreturn wrapperreturn decorator@repeat(times=3)def greet(name: str) -> str:return f"Hello, {name}!"# greet("Alice") returns ["Hello, Alice!", "Hello, Alice!", "Hello, Alice!"]
Class-Based Decorators
python
class CountCalls:"""Decorator that counts how many times a function is called."""def __init__(self, func: Callable):functools.update_wrapper(self, func)self.func = funcself.count = 0def __call__(self, *args, **kwargs):self.count += 1print(f"{self.func.__name__} has been called {self.count} times")return self.func(*args, **kwargs)@CountCallsdef process():pass# Each call to process() prints the call count
Concurrency Patterns
Threading for I/O-Bound Tasks
python
import concurrent.futuresimport threadingdef fetch_url(url: str) -> str:"""Fetch a URL (I/O-bound operation)."""import urllib.requestwith urllib.request.urlopen(url) as response:return response.read().decode()def fetch_all_urls(urls: list[str]) -> dict[str, str]:"""Fetch multiple URLs concurrently using threads."""with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:future_to_url = {executor.submit(fetch_url, url): url for url in urls}results = {}for future in concurrent.futures.as_completed(future_to_url):url = future_to_url[future]try:results[url] = future.result()except Exception as e:results[url] = f"Error: {e}"return results
Multiprocessing for CPU-Bound Tasks
python
def process_data(data: list[int]) -> int:"""CPU-intensive computation."""return sum(x ** 2 for x in data)def process_all(datasets: list[list[int]]) -> list[int]:"""Process multiple datasets using multiple processes."""with concurrent.futures.ProcessPoolExecutor() as executor:results = list(executor.map(process_data, datasets))return results
Async/Await for Concurrent I/O
python
import asyncioasync def fetch_async(url: str) -> str:"""Fetch a URL asynchronously."""import aiohttpasync with aiohttp.ClientSession() as session:async with session.get(url) as response:return await response.text()async def fetch_all(urls: list[str]) -> dict[str, str]:"""Fetch multiple URLs concurrently."""tasks = [fetch_async(url) for url in urls]results = await asyncio.gather(*tasks, return_exceptions=True)return dict(zip(urls, results))
Package Organization
Standard Project Layout
myproject/├── src/│ └── mypackage/│ ├── __init__.py│ ├── main.py│ ├── api/│ │ ├── __init__.py│ │ └── routes.py│ ├── models/│ │ ├── __init__.py│ │ └── user.py│ └── utils/│ ├── __init__.py│ └── helpers.py├── tests/│ ├── __init__.py│ ├── conftest.py│ ├── test_api.py│ └── test_models.py├── pyproject.toml├── README.md└── .gitignore
Import Conventions
python
# Good: Import order - stdlib, third-party, localimport osimport sysfrom pathlib import Pathimport requestsfrom fastapi import FastAPIfrom mypackage.models import Userfrom mypackage.utils import format_name# Good: Use isort for automatic import sorting# pip install isort
__init__.py for Package Exports
python
# mypackage/__init__.py"""mypackage - A sample Python package."""__version__ = "1.0.0"# Export main classes/functions at package levelfrom mypackage.models import User, Postfrom mypackage.utils import format_name__all__ = ["User", "Post", "format_name"]
Memory and Performance
Using __slots__ for Memory Efficiency
python
# Bad: Regular class uses __dict__ (more memory)class Point:def __init__(self, x: float, y: float):self.x = xself.y = y# Good: __slots__ reduces memory usageclass Point:__slots__ = ['x', 'y']def __init__(self, x: float, y: float):self.x = xself.y = y
Generator for Large Data
python
# Bad: Returns full list in memorydef read_lines(path: str) -> list[str]:with open(path) as f:return [line.strip() for line in f]# Good: Yields lines one at a timedef read_lines(path: str) -> Iterator[str]:with open(path) as f:for line in f:yield line.strip()
Avoid String Concatenation in Loops
python
# Bad: O(n²) due to string immutabilityresult = ""for item in items:result += str(item)# Good: O(n) using joinresult = "".join(str(item) for item in items)# Good: Using StringIO for buildingfrom io import StringIObuffer = StringIO()for item in items:buffer.write(str(item))result = buffer.getvalue()
Python Tooling Integration
Essential Commands
bash
# Code formattingblack .isort .# Lintingruff check .pylint mypackage/# Type checkingmypy .# Testingpytest --cov=mypackage --cov-report=html# Security scanningbandit -r .# Dependency managementpip-auditsafety check
pyproject.toml Configuration
toml
[project]name = "mypackage"version = "1.0.0"requires-python = ">=3.9"dependencies = ["requests>=2.31.0","pydantic>=2.0.0",][project.optional-dependencies]dev = ["pytest>=7.4.0","pytest-cov>=4.1.0","black>=23.0.0","ruff>=0.1.0","mypy>=1.5.0",][tool.black]line-length = 88target-version = ['py39'][tool.ruff]line-length = 88select = ["E", "F", "I", "N", "W"][tool.mypy]python_version = "3.9"warn_return_any = truewarn_unused_configs = truedisallow_untyped_defs = true[tool.pytest.ini_options]testpaths = ["tests"]addopts = "--cov=mypackage --cov-report=term-missing"
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
python
# Bad: Mutable default argumentsdef append_to(item, items=[]):items.append(item)return items# Good: Use None and create new listdef append_to(item, items=None):if items is None:items = []items.append(item)return items# Bad: Checking type with type()if type(obj) == list:process(obj)# Good: Use isinstanceif isinstance(obj, list):process(obj)# Bad: Comparing to None with ==if value == None:process()# Good: Use isif value is None:process()# Bad: from module import *from os.path import *# Good: Explicit importsfrom os.path import join, exists# Bad: Bare excepttry:risky_operation()except:pass# Good: Specific exceptiontry: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.