Python development principles and decision-making. Framework selection, async patterns, type hints, project structure. Teaches thinking, not copying.
Add this skill
npx mdskills install sickn33/python-patternsComprehensive Python decision-making guide with clear framework selection and async patterns
1---2name: python-patterns3description: Python development principles and decision-making. Framework selection, async patterns, type hints, project structure. Teaches thinking, not copying.4allowed-tools: Read, Write, Edit, Glob, Grep5---67# Python Patterns89> Python development principles and decision-making for 2025.10> **Learn to THINK, not memorize patterns.**1112---1314## ⚠️ How to Use This Skill1516This skill teaches **decision-making principles**, not fixed code to copy.1718- ASK user for framework preference when unclear19- Choose async vs sync based on CONTEXT20- Don't default to same framework every time2122---2324## 1. Framework Selection (2025)2526### Decision Tree2728```29What are you building?30│31├── API-first / Microservices32│ └── FastAPI (async, modern, fast)33│34├── Full-stack web / CMS / Admin35│ └── Django (batteries-included)36│37├── Simple / Script / Learning38│ └── Flask (minimal, flexible)39│40├── AI/ML API serving41│ └── FastAPI (Pydantic, async, uvicorn)42│43└── Background workers44 └── Celery + any framework45```4647### Comparison Principles4849| Factor | FastAPI | Django | Flask |50|--------|---------|--------|-------|51| **Best for** | APIs, microservices | Full-stack, CMS | Simple, learning |52| **Async** | Native | Django 5.0+ | Via extensions |53| **Admin** | Manual | Built-in | Via extensions |54| **ORM** | Choose your own | Django ORM | Choose your own |55| **Learning curve** | Low | Medium | Low |5657### Selection Questions to Ask:581. Is this API-only or full-stack?592. Need admin interface?603. Team familiar with async?614. Existing infrastructure?6263---6465## 2. Async vs Sync Decision6667### When to Use Async6869```70async def is better when:71├── I/O-bound operations (database, HTTP, file)72├── Many concurrent connections73├── Real-time features74├── Microservices communication75└── FastAPI/Starlette/Django ASGI7677def (sync) is better when:78├── CPU-bound operations79├── Simple scripts80├── Legacy codebase81├── Team unfamiliar with async82└── Blocking libraries (no async version)83```8485### The Golden Rule8687```88I/O-bound → async (waiting for external)89CPU-bound → sync + multiprocessing (computing)9091Don't:92├── Mix sync and async carelessly93├── Use sync libraries in async code94└── Force async for CPU work95```9697### Async Library Selection9899| Need | Async Library |100|------|---------------|101| HTTP client | httpx |102| PostgreSQL | asyncpg |103| Redis | aioredis / redis-py async |104| File I/O | aiofiles |105| Database ORM | SQLAlchemy 2.0 async, Tortoise |106107---108109## 3. Type Hints Strategy110111### When to Type112113```114Always type:115├── Function parameters116├── Return types117├── Class attributes118├── Public APIs119120Can skip:121├── Local variables (let inference work)122├── One-off scripts123├── Tests (usually)124```125126### Common Type Patterns127128```python129# These are patterns, understand them:130131# Optional → might be None132from typing import Optional133def find_user(id: int) -> Optional[User]: ...134135# Union → one of multiple types136def process(data: str | dict) -> None: ...137138# Generic collections139def get_items() -> list[Item]: ...140def get_mapping() -> dict[str, int]: ...141142# Callable143from typing import Callable144def apply(fn: Callable[[int], str]) -> str: ...145```146147### Pydantic for Validation148149```150When to use Pydantic:151├── API request/response models152├── Configuration/settings153├── Data validation154├── Serialization155156Benefits:157├── Runtime validation158├── Auto-generated JSON schema159├── Works with FastAPI natively160└── Clear error messages161```162163---164165## 4. Project Structure Principles166167### Structure Selection168169```170Small project / Script:171├── main.py172├── utils.py173└── requirements.txt174175Medium API:176├── app/177│ ├── __init__.py178│ ├── main.py179│ ├── models/180│ ├── routes/181│ ├── services/182│ └── schemas/183├── tests/184└── pyproject.toml185186Large application:187├── src/188│ └── myapp/189│ ├── core/190│ ├── api/191│ ├── services/192│ ├── models/193│ └── ...194├── tests/195└── pyproject.toml196```197198### FastAPI Structure Principles199200```201Organize by feature or layer:202203By layer:204├── routes/ (API endpoints)205├── services/ (business logic)206├── models/ (database models)207├── schemas/ (Pydantic models)208└── dependencies/ (shared deps)209210By feature:211├── users/212│ ├── routes.py213│ ├── service.py214│ └── schemas.py215└── products/216 └── ...217```218219---220221## 5. Django Principles (2025)222223### Django Async (Django 5.0+)224225```226Django supports async:227├── Async views228├── Async middleware229├── Async ORM (limited)230└── ASGI deployment231232When to use async in Django:233├── External API calls234├── WebSocket (Channels)235├── High-concurrency views236└── Background task triggering237```238239### Django Best Practices240241```242Model design:243├── Fat models, thin views244├── Use managers for common queries245├── Abstract base classes for shared fields246247Views:248├── Class-based for complex CRUD249├── Function-based for simple endpoints250├── Use viewsets with DRF251252Queries:253├── select_related() for FKs254├── prefetch_related() for M2M255├── Avoid N+1 queries256└── Use .only() for specific fields257```258259---260261## 6. FastAPI Principles262263### async def vs def in FastAPI264265```266Use async def when:267├── Using async database drivers268├── Making async HTTP calls269├── I/O-bound operations270└── Want to handle concurrency271272Use def when:273├── Blocking operations274├── Sync database drivers275├── CPU-bound work276└── FastAPI runs in threadpool automatically277```278279### Dependency Injection280281```282Use dependencies for:283├── Database sessions284├── Current user / Auth285├── Configuration286├── Shared resources287288Benefits:289├── Testability (mock dependencies)290├── Clean separation291├── Automatic cleanup (yield)292```293294### Pydantic v2 Integration295296```python297# FastAPI + Pydantic are tightly integrated:298299# Request validation300@app.post("/users")301async def create(user: UserCreate) -> UserResponse:302 # user is already validated303 ...304305# Response serialization306# Return type becomes response schema307```308309---310311## 7. Background Tasks312313### Selection Guide314315| Solution | Best For |316|----------|----------|317| **BackgroundTasks** | Simple, in-process tasks |318| **Celery** | Distributed, complex workflows |319| **ARQ** | Async, Redis-based |320| **RQ** | Simple Redis queue |321| **Dramatiq** | Actor-based, simpler than Celery |322323### When to Use Each324325```326FastAPI BackgroundTasks:327├── Quick operations328├── No persistence needed329├── Fire-and-forget330└── Same process331332Celery/ARQ:333├── Long-running tasks334├── Need retry logic335├── Distributed workers336├── Persistent queue337└── Complex workflows338```339340---341342## 8. Error Handling Principles343344### Exception Strategy345346```347In FastAPI:348├── Create custom exception classes349├── Register exception handlers350├── Return consistent error format351└── Log without exposing internals352353Pattern:354├── Raise domain exceptions in services355├── Catch and transform in handlers356└── Client gets clean error response357```358359### Error Response Philosophy360361```362Include:363├── Error code (programmatic)364├── Message (human readable)365├── Details (field-level when applicable)366└── NOT stack traces (security)367```368369---370371## 9. Testing Principles372373### Testing Strategy374375| Type | Purpose | Tools |376|------|---------|-------|377| **Unit** | Business logic | pytest |378| **Integration** | API endpoints | pytest + httpx/TestClient |379| **E2E** | Full workflows | pytest + DB |380381### Async Testing382383```python384# Use pytest-asyncio for async tests385386import pytest387from httpx import AsyncClient388389@pytest.mark.asyncio390async def test_endpoint():391 async with AsyncClient(app=app, base_url="http://test") as client:392 response = await client.get("/users")393 assert response.status_code == 200394```395396### Fixtures Strategy397398```399Common fixtures:400├── db_session → Database connection401├── client → Test client402├── authenticated_user → User with token403└── sample_data → Test data setup404```405406---407408## 10. Decision Checklist409410Before implementing:411412- [ ] **Asked user about framework preference?**413- [ ] **Chosen framework for THIS context?** (not just default)414- [ ] **Decided async vs sync?**415- [ ] **Planned type hint strategy?**416- [ ] **Defined project structure?**417- [ ] **Planned error handling?**418- [ ] **Considered background tasks?**419420---421422## 11. Anti-Patterns to Avoid423424### ❌ DON'T:425- Default to Django for simple APIs (FastAPI may be better)426- Use sync libraries in async code427- Skip type hints for public APIs428- Put business logic in routes/views429- Ignore N+1 queries430- Mix async and sync carelessly431432### ✅ DO:433- Choose framework based on context434- Ask about async requirements435- Use Pydantic for validation436- Separate concerns (routes → services → repos)437- Test critical paths438439---440441> **Remember**: Python patterns are about decision-making for YOUR specific context. Don't copy code—think about what serves your application best.442
Full transparency — inspect the skill content before installing.