Sign In Create Account
Type Hints

Type Hints & Documentation

Use type hints and comprehensive documentation for better code clarity and maintainability.

Basic Type Annotations

Always use type hints for function parameters, return values, and class attributes to improve code clarity and enable better IDE support.

Basic Type Annotations

from typing import Any

# ✅ Good type annotations
def process_user_data(
    username: str, 
    age: int, 
    is_active: bool = True
) -> dict[str, Any]:
    """Process user data and return formatted result."""
    return {
        "username": username.lower(),
        "age": age,
        "is_active": is_active,
        "processed_at": datetime.now()
    }

# ✅ Class with type hints
class DatabaseConnection:
    """Handle database connections with proper type hints."""

    def __init__(self, host: str, port: int, database: str):
        self.host: str = host
        self.port: int = port
        self.database: str = database
        self.connection: Connection | None = None

    def connect(self) -> bool:
        """Establish database connection."""
        try:
            # Connection logic
            return True
        except ConnectionError:
            return False

    def execute_query(self, query: str, params: list[Any] = None) -> list[dict]:
        """Execute query and return results."""
        if params is None:
            params = []
        # Query execution logic
        return []

Type Hints Benefits

  • IDE Support: Better autocomplete and error detection
  • Documentation: Self-documenting code interfaces
  • Refactoring: Safer code changes and refactoring
  • Team Communication: Clear expectations for function inputs/outputs

Advanced Type Annotations

Generic Types and Collections

Generic Types

from typing import Any
from collections.abc import Callable

# ✅ Generic collections
def process_items(items: list[str]) -> dict[str, int]:
    """Count character length for each item."""
    return {item: len(item) for item in items}

def filter_users(
    users: list[dict[str, Any]], 
    filter_func: Callable[[dict], bool]
) -> list[dict[str, Any]]:
    """Filter users based on provided function."""
    return [user for user in users if filter_func(user)]

# ✅ Optional and Union types
def get_user_by_id(user_id: int) -> dict[str, Any] | None:
    """Get user by ID, return None if not found."""
    # Database lookup logic
    return None  # or user dict

def parse_input(value: str | int | float) -> str:
    """Convert various input types to string."""
    return str(value)

# ✅ Tuple types
def get_coordinates() -> tuple[float, float]:
    """Return latitude and longitude coordinates."""
    return (40.7128, -74.0060)

def get_user_info() -> tuple[str, int, bool]:
    """Return username, age, and active status."""
    return ("john_doe", 30, True)

Custom Types and TypedDict

Custom Types

from typing import TypedDict, NewType
from datetime import datetime

# ✅ Custom type aliases
UserId = NewType('UserId', int)
EmailAddress = NewType('EmailAddress', str)

# ✅ TypedDict for structured dictionaries
class UserData(TypedDict):
    id: UserId
    username: str
    email: EmailAddress
    created_at: datetime
    is_active: bool

class ApiResponse(TypedDict):
    success: bool
    data: UserData | None
    error: str | None

# ✅ Using custom types
def create_user(
    username: str, 
    email: EmailAddress
) -> UserData:
    """Create a new user with proper type structure."""
    return UserData(
        id=UserId(generate_id()),
        username=username,
        email=email,
        created_at=datetime.now(),
        is_active=True
    )

def send_email(user_id: UserId, email: EmailAddress, message: str) -> bool:
    """Send email to specific user."""
    # Email sending logic
    return True

Documentation Standards

Use Google-style or NumPy-style docstrings for comprehensive function and class documentation.

Google-Style Docstrings

Google-Style Documentation

from typing import Any

def authenticate_user(username: str, password: str, remember_me: bool = False) -> dict[str, Any]:
    """Authenticate user with provided credentials.
    
    This function verifies user credentials against the database and returns
    authentication result with user information if successful.
    
    Args:
        username: The username for authentication. Must be a non-empty string.
        password: The user's password. Will be hashed before comparison.
        remember_me: Whether to create a persistent session. Defaults to False.
    
    Returns:
        A dictionary containing authentication result:
        {
            'success': bool,
            'user': dict or None,
            'token': str or None,
            'expires_at': datetime or None
        }
    
    Raises:
        ValueError: If username or password is empty.
        DatabaseError: If database connection fails.
        
    Example:
        >>> result = authenticate_user("john_doe", "secure_password")
        >>> if result['success']:
        ...     print(f"Welcome, {result['user']['name']}!")
    """
    if not username or not password:
        raise ValueError("Username and password cannot be empty")
    
    # Authentication logic here
    return {
        'success': True,
        'user': {'id': 1, 'name': 'John Doe'},
        'token': 'jwt_token_here',
        'expires_at': datetime.now() + timedelta(hours=24)
    }

Class Documentation

Class Documentation

class PaymentProcessor:
    """Process various payment methods for e-commerce transactions.
    
    This class handles payment processing for multiple payment providers
    including credit cards, PayPal, and bank transfers. It provides a
    unified interface for payment operations with proper error handling
    and logging.
    
    Attributes:
        api_key: The API key for payment provider authentication.
        provider: The payment provider name (e.g., 'stripe', 'paypal').
        sandbox_mode: Whether to use sandbox/test mode for payments.
        
    Example:
        >>> processor = PaymentProcessor(api_key="key123", provider="stripe")
        >>> result = processor.process_payment(amount=29.99, method="credit_card")
        >>> if result.success:
        ...     print(f"Payment processed: {result.transaction_id}")
    """
    
    def __init__(self, api_key: str, provider: str, sandbox_mode: bool = False):
        """Initialize payment processor.
        
        Args:
            api_key: API key for payment provider authentication.
            provider: Payment provider name ('stripe', 'paypal', 'square').
            sandbox_mode: Use sandbox mode for testing. Defaults to False.
            
        Raises:
            ValueError: If api_key is empty or provider is not supported.
        """
        if not api_key:
            raise ValueError("API key cannot be empty")
            
        supported_providers = ['stripe', 'paypal', 'square']
        if provider not in supported_providers:
            raise ValueError(f"Provider must be one of: {supported_providers}")
        
        self.api_key = api_key
        self.provider = provider
        self.sandbox_mode = sandbox_mode
        self._session = self._create_session()
    
    def process_payment(
        self, 
        amount: float, 
        currency: str = "USD",
        method: str = "credit_card"
    ) -> PaymentResult:
        """Process a payment transaction.
        
        Args:
            amount: Payment amount in the specified currency.
            currency: Currency code (ISO 4217). Defaults to "USD".
            method: Payment method ('credit_card', 'paypal', 'bank_transfer').
            
        Returns:
            PaymentResult object containing transaction details and status.
            
        Raises:
            PaymentError: If payment processing fails.
            ValueError: If amount is negative or currency is invalid.
        """
        # Payment processing logic
        pass

Type Checking with mypy

Use mypy for static type checking to catch type-related errors before runtime.

mypy Configuration

# pyproject.toml
[tool.mypy]
python_version = "3.12"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
check_untyped_defs = true
disallow_untyped_decorators = true
no_implicit_optional = true
warn_redundant_casts = true
warn_unused_ignores = true
warn_no_return = true
warn_unreachable = true
strict_equality = true

# Per-module options
[[tool.mypy.overrides]]
module = "tests.*"
ignore_errors = true

[[tool.mypy.overrides]]
module = "third_party_library.*"
ignore_missing_imports = true

Type Checking Best Practices

  • • Run mypy as part of your CI/CD pipeline
  • • Use # type: ignore sparingly and with comments
  • • Enable strict mode gradually for existing codebases
  • • Use stub files (.pyi) for third-party libraries without types

Common Type Annotation Patterns

Common Patterns

from typing import Protocol, runtime_checkable
from abc import ABC, abstractmethod

# ✅ Protocol for duck typing
@runtime_checkable
class Drawable(Protocol):
    """Protocol for drawable objects."""
    
    def draw(self) -> None:
        """Draw the object."""
        ...

def render_object(obj: Drawable) -> None:
    """Render any drawable object."""
    obj.draw()

# ✅ Abstract base class
class DataProcessor(ABC):
    """Abstract base class for data processors."""
    
    @abstractmethod
    def process(self, data: list[dict]) -> list[dict]:
        """Process data and return results."""
        pass
    
    def validate_data(self, data: list[dict]) -> bool:
        """Validate input data format."""
        return all(isinstance(item, dict) for item in data)

# ✅ Generic class
from typing import Generic, TypeVar

T = TypeVar('T')

class Repository(Generic[T]):
    """Generic repository pattern."""
    
    def __init__(self, model_class: type[T]):
        self.model_class = model_class
        self._items: list[T] = []
    
    def add(self, item: T) -> None:
        """Add item to repository."""
        self._items.append(item)
    
    def find_by_id(self, id: int) -> T | None:
        """Find item by ID."""
        # Search logic
        return None
    
    def get_all(self) -> list[T]:
        """Get all items."""
        return self._items.copy()

# Usage
user_repo = Repository[User](User)
product_repo = Repository[Product](Product)

Advanced Type Features

  • Protocols: Define interfaces without inheritance
  • Generic Classes: Create reusable, type-safe containers
  • Abstract Base Classes: Enforce implementation contracts
  • Type Variables: Create flexible, type-safe functions