pythonintermediate

Python Dataclass Advanced Patterns

Advanced dataclass usage with validation, post-init processing, slots, and frozen instances.

python
from dataclasses import dataclass, field, asdict, replace
from typing import Optional
from datetime import datetime


@dataclass(frozen=True, slots=True)
class Money:
    amount: int  # cents
    currency: str = "USD"

    @property
    def dollars(self) -> float:
        return self.amount / 100

    def __str__(self) -> str:
        return f"${self.dollars:,.2f} {self.currency}"


@dataclass
class User:
    name: str
    email: str
    age: int
    tags: list[str] = field(default_factory=list)
    created_at: datetime = field(default_factory=datetime.now)
    _id: str = field(init=False, repr=False)

    def __post_init__(self):
        if self.age < 0 or self.age > 150:
            raise ValueError(f"Invalid age: {self.age}")
        self.email = self.email.lower().strip()
        self._id = f"user_{hash(self.email)}"[:16]


@dataclass
class Config:
    host: str = "localhost"
    port: int = 8080
    debug: bool = False
    workers: int = 4
    db_url: Optional[str] = None

    @classmethod
    def from_env(cls, prefix: str = "APP") -> "Config":
        import os
        return cls(
            host=os.getenv(f"{prefix}_HOST", cls.host),
            port=int(os.getenv(f"{prefix}_PORT", str(cls.port))),
            debug=os.getenv(f"{prefix}_DEBUG", "").lower() == "true",
        )


# Usage
price = Money(1999)
print(price)  # $19.99 USD

user = User("Alice", "  Alice@Email.COM  ", 30, ["admin"])
print(asdict(user))  # Convert to dict

updated = replace(user, name="Bob")  # Immutable-style update
config = Config.from_env()

Use Cases

  • Type-safe data models without ORMs
  • Configuration management with defaults
  • Immutable value objects with validation

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.