Pythonic idioms, PEP 8 standards, type hints, and best practices for building robust, efficient, and maintainable Python applications.
From clarcnpx claudepluginhub marvinrichter/clarc --plugin clarcThis skill uses the workspace's default tool permissions.
Designs and optimizes AI agent action spaces, tool definitions, observation formats, error recovery, and context for higher task completion rates.
Enables AI agents to execute x402 payments with per-task budgets, spending controls, and non-custodial wallets via MCP tools. Use when agents pay for APIs, services, or other agents.
Compares coding agents like Claude Code and Aider on custom YAML-defined codebase tasks using git worktrees, measuring pass rate, cost, time, and consistency.
Idiomatic Python patterns and best practices for building robust, efficient, and maintainable applications.
Python prioritizes readability. Code should be obvious and easy to understand.
# Good: Clear and readable
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]
# Bad: Clever but confusing
def get_active_users(u):
return [x for x in u if x.a]
Avoid magic; be clear about what your code does.
# Good: Explicit configuration
import logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
# Bad: Hidden side effects
import some_module
some_module.setup() # What does this do?
Python prefers exception handling over checking conditions.
# Good: EAFP style
def get_value(dictionary: dict, key: str) -> Any:
try:
return dictionary[key]
except KeyError:
return default_value
# Bad: LBYL (Look Before You Leap) style
def get_value(dictionary: dict, key: str) -> Any:
if key in dictionary:
return dictionary[key]
else:
return default_value
When to use which: Use EAFP for external resources (files, APIs, DB) where failures are expected and informative. Use LBYL for simple attribute checks on known objects where a conditional read is cheaper than exception handling.
Use built-in generic types directly (list[str], dict[str, Any], X | None) — no typing imports for basic annotations:
from typing import Any
def process_user(user_id: str, data: dict[str, Any], active: bool = True) -> User | None:
return User(user_id, data) if active else None
def process_items(items: list[str]) -> dict[str, int]:
return {item: len(item) for item in items}
from typing import Any
# Type alias (PEP 695 — Python 3.12+)
type JSON = dict[str, Any] | list[Any] | str | int | float | bool | None
def parse_json(data: str) -> JSON:
return json.loads(data)
# Generic functions with new syntax (Python 3.12+)
def first[T](items: list[T]) -> T | None:
"""Return the first item or None if list is empty."""
return items[0] if items else None
# Generic classes (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:
return self._items.pop()
from typing import Protocol
class 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)
# Good: Catch specific exceptions
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
# Bad: Bare except
def load_config(path: str) -> Config:
try:
with open(path) as f:
return Config.from_json(f.read())
except:
return None # Silent failure!
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
# Usage
def 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
Always use with for resource management (files, DB connections, locks). Prefer @contextmanager for simple cases; use __enter__/__exit__ classes for stateful resources.
from contextlib import contextmanager
@contextmanager
def timer(name: str):
"""Context manager to time a block of code."""
start = time.perf_counter()
yield
elapsed = time.perf_counter() - start
print(f"{name} took {elapsed:.4f} seconds")
# Usage
with timer("data processing"):
process_large_dataset()
class DatabaseTransaction:
def __init__(self, connection):
self.connection = connection
def __enter__(self):
self.connection.begin_transaction()
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
# Usage
with DatabaseTransaction(conn):
user = conn.create_user(user_data)
conn.create_profile(user.id, profile_data)
# List comprehension — prefer over manual loops
names = [user.name for user in users if user.is_active]
# Generator expression — lazy, no intermediate list (O(1) memory)
total = sum(x * x for x in range(1_000_000))
# Generator function — stream large data without loading all into memory
def read_large_file(path: str) -> Iterator[str]:
with open(path) as f:
for line in f:
yield line.strip()
Keep comprehensions simple — if a comprehension needs more than one if or a nested loop, use a regular function instead.
from dataclasses import dataclass, field
from datetime import datetime
@dataclass
class User:
"""Automatic __init__, __repr__, __eq__. Use __post_init__ for validation."""
id: str
name: str
email: str
created_at: datetime = field(default_factory=datetime.now)
is_active: bool = True
def __post_init__(self):
if "@" not in self.email:
raise ValueError(f"Invalid email: {self.email}")
For advanced patterns —
NamedTuplewith methods, function decorators, parameterized decorators, class-based decorators, concurrency (threading, multiprocessing, async/await), hexagonal architecture with FastAPI, and memory optimization — seepython-patterns-advanced.
Wrong:
def append_item(item, collection=[]): # list is created once at definition time
collection.append(item)
return collection
append_item("a") # ["a"]
append_item("b") # ["a", "b"] — unexpected: shares the same list
Correct:
def append_item(item, collection=None):
if collection is None:
collection = []
collection.append(item)
return collection
Why: Default argument values are evaluated once when the function is defined; mutable defaults accumulate state across calls.
Exception (or bare except) and Swallowing ItWrong:
def load_user(user_id: str) -> User | None:
try:
return db.find(user_id)
except Exception:
return None # hides programming errors, network failures, everything
Correct:
def load_user(user_id: str) -> User | None:
try:
return db.find(user_id)
except UserNotFoundError:
return None
except DatabaseError as e:
raise ServiceUnavailableError("Database unreachable") from e
Why: Catching Exception silently swallows bugs and infrastructure failures, making them impossible to observe or recover from correctly.
+ in a LoopWrong:
def build_csv(rows: list[dict]) -> str:
result = ""
for row in rows:
result += ",".join(str(v) for v in row.values()) + "\n"
return result
Correct:
def build_csv(rows: list[dict]) -> str:
lines = [",".join(str(v) for v in row.values()) for row in rows]
return "\n".join(lines) + "\n"
Why: String concatenation in a loop is O(n²) because each + creates a new string; str.join is O(n).
Optional[X] Instead of X | None in Modern PythonWrong:
from typing import Optional
def find_user(user_id: str) -> Optional[User]:
...
Correct:
def find_user(user_id: str) -> User | None:
...
Why: X | None (PEP 604, Python 3.10+) is the idiomatic modern syntax; Optional requires a typing import and is now considered legacy style.
type(x) == SomeClass Instead of isinstanceWrong:
def process(value):
if type(value) == int:
return value * 2
Correct:
def process(value):
if isinstance(value, int):
return value * 2
Why: type(x) == SomeClass breaks for subclasses and does not respect the Python type hierarchy; isinstance handles inheritance correctly.
For advanced patterns — concurrency (threading, multiprocessing, async/await), hexagonal architecture with FastAPI (full working code, Protocol ports, DI wiring, RFC 7807 error handling, tests), memory optimization, tooling (
pyproject.toml, ruff, mypy), and anti-patterns — see skill:python-patterns-advanced.