# Python Patterns

> <!-- AUTO-GENERATED by export-skills.py — DO NOT EDIT -->

- Skill: `frank-luongt/python-patterns` (Agent Skill)
- Install (CLI): `npx skillmds@latest add frank-luongt/python-patterns`
- Raw SKILL.md: https://api.skillmd.com/api/skills/frank-luongt/python-patterns/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: frank-luongt (https://skillmd.com/u/frank-luongt)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/frank-luongt/python-patterns

---

<!-- AUTO-GENERATED by export-skills.py — DO NOT EDIT -->
---
name: python-patterns
description: Python development principles and decision-making. Framework selection, async patterns, type hints, project structure. Use when making Python architecture decisions, choosing frameworks, or structuring Python projects.
tags: [python, fastapi, django, patterns]
---

# Python Patterns

> Python development principles and decision-making for 2025.
> **Learn to THINK, not memorize patterns.**

## When to Use

Use this skill when making Python architecture decisions, choosing frameworks, designing async patterns, or structuring Python projects.

---

## How to Use This Skill

This skill teaches **decision-making principles**, not fixed code to copy.

- ASK user for framework preference when unclear
- Choose async vs sync based on CONTEXT
- Don't default to same framework every time

---

## 1. Framework Selection (2025)

### Decision Tree

```
What are you building?
|
+-- API-first / Microservices
|   +-- FastAPI (async, modern, fast)
|
+-- Full-stack web / CMS / Admin
|   +-- Django (batteries-included)
|
+-- Simple / Script / Learning
|   +-- Flask (minimal, flexible)
|
+-- AI/ML API serving
|   +-- FastAPI (Pydantic, async, uvicorn)
|
+-- Background workers
    +-- Celery + any framework
```

### Comparison Principles

| Factor | FastAPI | Django | Flask |
|--------|---------|--------|-------|
| **Best for** | APIs, microservices | Full-stack, CMS | Simple, learning |
| **Async** | Native | Django 5.0+ | Via extensions |
| **Admin** | Manual | Built-in | Via extensions |
| **ORM** | Choose your own | Django ORM | Choose your own |
| **Learning curve** | Low | Medium | Low |

### Selection Questions to Ask:
1. Is this API-only or full-stack?
2. Need admin interface?
3. Team familiar with async?
4. Existing infrastructure?

---

## 2. Async vs Sync Decision

### When to Use Async

```
async def is better when:
+-- I/O-bound operations (database, HTTP, file)
+-- Many concurrent connections
+-- Real-time features
+-- Microservices communication
+-- FastAPI/Starlette/Django ASGI

def (sync) is better when:
+-- CPU-bound operations
+-- Simple scripts
+-- Legacy codebase
+-- Team unfamiliar with async
+-- Blocking libraries (no async version)
```

### The Golden Rule

```
I/O-bound -> async (waiting for external)
CPU-bound -> sync + multiprocessing (computing)

Don't:
+-- Mix sync and async carelessly
+-- Use sync libraries in async code
+-- Force async for CPU work
```

### Async Library Selection

| Need | Async Library |
|------|---------------|
| HTTP client | httpx |
| PostgreSQL | asyncpg |
| Redis | aioredis / redis-py async |
| File I/O | aiofiles |
| Database ORM | SQLAlchemy 2.0 async, Tortoise |

---

## 3. Type Hints Strategy

### When to Type

```
Always type:
+-- Function parameters
+-- Return types
+-- Class attributes
+-- Public APIs

Can skip:
+-- Local variables (let inference work)
+-- One-off scripts
+-- Tests (usually)
```

### Common Type Patterns

```python
# These are patterns, understand them:

# Optional -> might be None
from typing import Optional
def find_user(id: int) -> Optional[User]: ...

# Union -> one of multiple types
def process(data: str | dict) -> None: ...

# Generic collections
def get_items() -> list[Item]: ...
def get_mapping() -> dict[str, int]: ...

# Callable
from typing import Callable
def apply(fn: Callable[[int], str]) -> str: ...
```

### Pydantic for Validation

```
When to use Pydantic:
+-- API request/response models
+-- Configuration/settings
+-- Data validation
+-- Serialization

Benefits:
+-- Runtime validation
+-- Auto-generated JSON schema
+-- Works with FastAPI natively
+-- Clear error messages
```

---

## 4. Project Structure Principles

### Structure Selection

```
Small project / Script:
+-- main.py
+-- utils.py
+-- requirements.txt

Medium API:
+-- app/
|   +-- __init__.py
|   +-- main.py
|   +-- models/
|   +-- routes/
|   +-- services/
|   +-- schemas/
+-- tests/
+-- pyproject.toml

Large application:
+-- src/
|   +-- myapp/
|       +-- core/
|       +-- api/
|       +-- services/
|       +-- models/
|       +-- ...
+-- tests/
+-- pyproject.toml
```

### FastAPI Structure Principles

```
Organize by feature or layer:

By layer:
+-- routes/ (API endpoints)
+-- services/ (business logic)
+-- models/ (database models)
+-- schemas/ (Pydantic models)
+-- dependencies/ (shared deps)

By feature:
+-- users/
|   +-- routes.py
|   +-- service.py
|   +-- schemas.py
+-- products/
    +-- ...
```

---

## 5. Django Principles (2025)

### Django Async (Django 5.0+)

```
Django supports async:
+-- Async views
+-- Async middleware
+-- Async ORM (limited)
+-- ASGI deployment

When to use async in Django:
+-- External API calls
+-- WebSocket (Channels)
+-- High-concurrency views
+-- Background task triggering
```

### Django Best Practices

```
Model design:
+-- Fat models, thin views
+-- Use managers for common queries
+-- Abstract base classes for shared fields

Views:
+-- Class-based for complex CRUD
+-- Function-based for simple endpoints
+-- Use viewsets with DRF

Queries:
+-- select_related() for FKs
+-- prefetch_related() for M2M
+-- Avoid N+1 queries
+-- Use .only() for specific fields
```

---

## 6. FastAPI Principles

### async def vs def in FastAPI

```
Use async def when:
+-- Using async database drivers
+-- Making async HTTP calls
+-- I/O-bound operations
+-- Want to handle concurrency

Use def when:
+-- Blocking operations
+-- Sync database drivers
+-- CPU-bound work
+-- FastAPI runs in threadpool automatically
```

### Dependency Injection

```
Use dependencies for:
+-- Database sessions
+-- Current user / Auth
+-- Configuration
+-- Shared resources

Benefits:
+-- Testability (mock dependencies)
+-- Clean separation
+-- Automatic cleanup (yield)
```

### Pydantic v2 Integration

```python
# FastAPI + Pydantic are tightly integrated:

# Request validation
@app.post("/users")
async def create(user: UserCreate) -> UserResponse:
    # user is already validated
    ...

# Response serialization
# Return type becomes response schema
```

---

## 7. Background Tasks

### Selection Guide

| Solution | Best For |
|----------|----------|
| **BackgroundTasks** | Simple, in-process tasks |
| **Celery** | Distributed, complex workflows |
| **ARQ** | Async, Redis-based |
| **RQ** | Simple Redis queue |
| **Dramatiq** | Actor-based, simpler than Celery |

### When to Use Each

```
FastAPI BackgroundTasks:
+-- Quick operations
+-- No persistence needed
+-- Fire-and-forget
+-- Same process

Celery/ARQ:
+-- Long-running tasks
+-- Need retry logic
+-- Distributed workers
+-- Persistent queue
+-- Complex workflows
```

---

## 8. Error Handling Principles

### Exception Strategy

```
In FastAPI:
+-- Create custom exception classes
+-- Register exception handlers
+-- Return consistent error format
+-- Log without exposing internals

Pattern:
+-- Raise domain exceptions in services
+-- Catch and transform in handlers
+-- Client gets clean error response
```

### Error Response Philosophy

```
Include:
+-- Error code (programmatic)
+-- Message (human readable)
+-- Details (field-level when applicable)
+-- NOT stack traces (security)
```

---

## 9. Testing Principles

### Testing Strategy

| Type | Purpose | Tools |
|------|---------|-------|
| **Unit** | Business logic | pytest |
| **Integration** | API endpoints | pytest + httpx/TestClient |
| **E2E** | Full workflows | pytest + DB |

### Async Testing

```python
# Use pytest-asyncio for async tests

import pytest
from httpx import AsyncClient

@pytest.mark.asyncio
async def test_endpoint():
    async with AsyncClient(app=app, base_url="http://test") as client:
        response = await client.get("/users")
        assert response.status_code == 200
```

### Fixtures Strategy

```
Common fixtures:
+-- db_session -> Database connection
+-- client -> Test client
+-- authenticated_user -> User with token
+-- sample_data -> Test data setup
```

---

## 10. Decision Checklist

Before implementing:

- [ ] **Asked user about framework preference?**
- [ ] **Chosen framework for THIS context?** (not just default)
- [ ] **Decided async vs sync?**
- [ ] **Planned type hint strategy?**
- [ ] **Defined project structure?**
- [ ] **Planned error handling?**
- [ ] **Considered background tasks?**

---

## 11. Anti-Patterns to Avoid

### DON'T:
- Default to Django for simple APIs (FastAPI may be better)
- Use sync libraries in async code
- Skip type hints for public APIs
- Put business logic in routes/views
- Ignore N+1 queries
- Mix async and sync carelessly

### DO:
- Choose framework based on context
- Ask about async requirements
- Use Pydantic for validation
- Separate concerns (routes -> services -> repos)
- Test critical paths

---

> **Remember**: Python patterns are about decision-making for YOUR specific context. Don't copy code--think about what serves your application best.

<!-- Source: .faos/custom/skills/backend/python-patterns/SKILL.md -->

