python-pro
Python Pro
Guidelines for writing clean, performant, and idiomatic Python code.
Core Principles
- Pythonic code - Follow PEP 8, use Python idioms
- Composition over inheritance - Prefer mixins and protocols
- Explicit is better than implicit - Clear error handling
- Generators for efficiency - Lazy evaluation for large datasets
- Type hints everywhere - Enable static analysis with mypy
Code Patterns
Type Hints (Python 3.10+)
from typing import Protocol, TypeVar, Generic
from collections.abc import Callable, Iterator
T = TypeVar('T')
class Repository(Protocol[T]):
def get(self, id: str) -> T | None: ...
def save(self, item: T) -> None: ...
def process_items[T](items: list[T], fn: Callable[[T], T]) -> Iterator[T]:
for item in items:
yield fn(item)
Context Managers
from contextlib import contextmanager
from typing import Generator
@contextmanager
def managed_resource(name: str) -> Generator[Resource, None, None]:
resource = Resource(name)
try:
yield resource
finally:
resource.cleanup()
Decorators with Proper Typing
from functools import wraps
from typing import ParamSpec, TypeVar, Callable
P = ParamSpec('P')
R = TypeVar('R')
def retry(max_attempts: int = 3) -> Callable[[Callable[P, R]], Callable[P, R]]:
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
raise RuntimeError("Unreachable")
return wrapper
return decorator
Async Patterns
import asyncio
from typing import AsyncIterator
async def fetch_all[T](urls: list[str], parse: Callable[[str], T]) -> list[T]:
async with aiohttp.ClientSession() as session:
tasks = [fetch_one(session, url, parse) for url in urls]
return await asyncio.gather(*tasks)
async def stream_data() -> AsyncIterator[bytes]:
async with aiofiles.open('large.csv', 'rb') as f:
async for chunk in f:
yield chunk
Custom Exceptions
from dataclasses import dataclass
@dataclass
class ValidationError(Exception):
field: str
message: str
value: object = None
def __str__(self) -> str:
return f"{self.field}: {self.message} (got {self.value!r})"
Testing with Pytest
Fixtures and Parametrization
import pytest
from typing import Generator
@pytest.fixture
def db_session() -> Generator[Session, None, None]:
session = Session()
yield session
session.rollback()
@pytest.fixture
def sample_user(db_session: Session) -> User:
user = User(name="test", email="test@example.com")
db_session.add(user)
return user
@pytest.mark.parametrize("input,expected", [
("hello", "HELLO"),
("World", "WORLD"),
("", ""),
])
def test_uppercase(input: str, expected: str) -> None:
assert input.upper() == expected
Async Testing
import pytest
@pytest.mark.asyncio
async def test_fetch_data() -> None:
result = await fetch_data("https://api.example.com")
assert result.status == "success"
Project Structure
project/
├── pyproject.toml # Modern Python config
├── src/
│ └── package/
│ ├── __init__.py
│ ├── py.typed # PEP 561 marker
│ ├── domain/ # Business logic
│ ├── services/ # Application services
│ └── adapters/ # External integrations
├── tests/
│ ├── conftest.py # Shared fixtures
│ ├── unit/
│ └── integration/
└── .python-version # pyenv version
pyproject.toml Template
[project]
name = "myproject"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = []
[project.optional-dependencies]
dev = ["pytest>=8.0", "mypy>=1.8", "ruff>=0.2"]
[tool.ruff]
line-length = 100
target-version = "py311"
[tool.ruff.lint]
select = ["E", "F", "I", "UP", "B", "SIM"]
[tool.mypy]
strict = true
python_version = "3.11"
[tool.pytest.ini_options]
asyncio_mode = "auto"
testpaths = ["tests"]
Performance Tips
- Use
__slots__for data classes with many instances - Prefer
dict.get()overtry/except KeyError - Use
itertoolsfor efficient iteration - Profile with
cProfileandline_profiler - Use
functools.lru_cachefor expensive pure functions
Common Anti-Patterns to Avoid
- Mutable default arguments:
def f(items=[])→def f(items=None) - Bare
except:clauses → Always specify exception type - Using
type()for comparisons → Useisinstance() - String concatenation in loops → Use
"".join()or f-strings - Ignoring return values → Handle or explicitly discard with
_
More from arosenkranz/claude-code-config
session-log
Document conversation accomplishments in Obsidian vault with frontmatter, code changes, learning notes, and next steps.
6evolve
Cluster related instincts into skills, commands, or agents
6agent-browser
Browser automation CLI for AI agents. Use when the user needs to interact with websites, including navigating pages, filling forms, clicking buttons, taking screenshots, extracting data, testing web apps, or automating any browser task. Triggers include requests to "open a website", "fill out a form", "click a button", "take a screenshot", "scrape data from a page", "test this web app", "login to a site", "automate browser actions", or any task requiring programmatic web interaction.
4instinct-status
Show all learned instincts with their confidence levels, grouped by domain.
4build-fix
Incrementally fix TypeScript and build errors one at a time, re-running the build after each fix to verify resolution.
2learning-guide
Guide technical learning through hands-on projects and clear explanations. Use when exploring new technologies, creating learning paths, building practice projects, or understanding new concepts like Go, Terraform, Kubernetes, or AWS.
2