python-code-patterns

ederheisler's avatarfrom ederheisler

Python code style and type hinting patterns. Use when writing or reviewing Python code to ensure consistent, modern type annotations and clean code structure. Covers modern type hints, import organization, comment practices, and docstring conventions.

0stars🔀0forks📁View on GitHub🕐Updated Jan 11, 2026

When & Why to Use This Skill

This Claude skill streamlines Python development by enforcing modern coding standards, PEP 585 type hints, and clean architectural patterns. It solves the problem of inconsistent codebases by providing clear rules for import organization, docstring conventions, and the separation of business logic from API routers, ensuring high-quality, maintainable, and type-safe Python applications.

Use Cases

  • Refactoring legacy Python codebases to adopt modern PEP 585 type hints and remove deprecated 'typing' module imports.
  • Enforcing architectural integrity in FastAPI or Flask projects by migrating business logic from routers into dedicated service layers.
  • Automating code reviews to identify and eliminate redundant comments, improper import guards, and unnecessary type-ignore flags.
  • Standardizing project documentation by ensuring docstrings focus on logic and edge cases rather than repeating type information already present in signatures.
namepython-code-patterns
descriptionPython code style and type hinting patterns. Use when writing or reviewing Python code to ensure consistent, modern type annotations, clean code structure, and proper separation of concerns. Covers modern type hints, import organization, router-service separation, comment practices, and docstring conventions.
authoreder
version"1.2"

Python Code Patterns

Exception: Tests Directory

All these rules can be relaxed in the tests/ directory. Go wild there - write whatever helps you test effectively.

Type Hints

Use modern Python type hints (PEP 585):

DO:

def process(items: list[str]) -> dict[str, int]:
    ...

data: list[dict[str, Any]] = []
```text

**DON'T:**

```python
from typing import List, Union, Optional

def process(items: List[str]) -> Dict[str, int]:
    ...

data: List[Dict[str, Any]] = []

Use lowercase built-in types: list, dict, tuple, set, frozenset, type, bytes

Import Organization

DO:

from typing import Any

def my_function(data: Any) -> None:
    ...
```text

**DON'T:**

```python
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from typing import Any

def my_function(data: Any) -> None:
    ...
  • Never use TYPE_CHECKING guards
  • Never use imports not at the top level (inside functions, classes, etc.)
  • If you have circular imports, your code structure needs refactoring
  • TYPE_CHECKING guards and late imports are code smells indicating poor architecture

Type Checking

Avoid # type: ignore as much as possible:

  • Only use as a last resort when there's a genuine limitation in type checking
  • If you find yourself needing # type: ignore frequently, reconsider your type annotations
  • Comment why it's needed when you must use it:
result: int = some_complex_function()  # type: ignore  # external library has incorrect type stub
```text

## Comments

Don't add obvious/redundant comments:

**DON'T:**

```python
id: str  # id
name: str  # the name
count: int  # number of items

# loop through items
for item in items:
    # process the item
    process(item)

Comments should explain why, not what.

Router-Service Separation

Keep routers free of business logic:

DO:

from fastapi import APIRouter

router = APIRouter()

@router.get("/items/{item_id}")
async def get_item(item_id: int):
    return await service.get_item(item_id)

DON'T:

from fastapi import APIRouter

router = APIRouter()

@router.get("/items/{item_id}")
async def get_item(item_id: int):
    if item_id <= 0:
        raise HTTPException(status_code=400, detail="Invalid item ID")
    
    item = db.query(Item).filter(Item.id == item_id).first()
    if not item:
        raise HTTPException(status_code=404, detail="Item not found")
    
    return item
  • Routers should only call service functions and return results
  • Validation, checks, business logic belong in the service layer
  • Services raise HTTPExceptions directly - let them bubble up through routers
  • Services contain domain logic and return domain objects or primitives

Service layer handles:

  • Input validation and sanitization
  • Business rules and constraints
  • Database operations and queries
  • Error conditions and exceptions raising

Router layer handles:

  • HTTP method routing
  • Delegating to service functions
  • Returning service results directly
  • No business logic or validation

Docstrings

Keep docstrings concise and focused:

DON'T:

def process_data(data: list[str]) -> dict[str, int]:
    """
    Process data.
    
    Args:
        data: The data to process
        
    Returns:
        dict[str, int]: The processed data
    """
    ...
```text

**DO:**

```python
def process_data(data: list[str]) -> dict[str, int]:
    """
    Converts string data to word frequency counts, ignoring case and punctuation.
    """
    ...
  • Type information already in signature - don't repeat it
  • Only include non-obvious information
  • Explain edge cases, constraints, algorithm choices, or complex behavior

When to Apply

Apply these patterns whenever:

  • Writing new Python code (outside tests/)
  • Reviewing or refactoring existing Python code (outside tests/)
  • Updating type annotations (outside tests/)
  • Adding or modifying imports (outside tests/)
  • Implementing API routers or service layer functions