Claude-skill-registry dataclass-patterns

Dataclass patterns including frozen dataclasses, slots, immutability, and value objects. Activated when designing data classes or value types.

install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/dataclass-patterns" ~/.claude/skills/majiayu000-claude-skill-registry-dataclass-patterns && rm -rf "$T"
manifest: skills/data/dataclass-patterns/SKILL.md
source content

Dataclass patterns

Purpose

Guide for designing dataclasses including frozen (immutable) dataclasses, slots optimization, validation, and value object patterns.

When to use

This skill activates when:

  • Creating data classes
  • Designing immutable value objects
  • Optimizing memory usage with slots
  • Implementing validation in dataclasses
  • Using dataclass features like field factories

Core patterns

Frozen immutable dataclass

from dataclasses import dataclass

@dataclass(frozen=True, slots=True)
class Point:
    """Immutable point with memory-efficient slots."""
    x: float
    y: float

# Usage
p = Point(1.0, 2.0)
# p.x = 3.0  # Raises FrozenInstanceError

With validation

from dataclasses import dataclass

@dataclass(frozen=True, slots=True)
class PositivePoint:
    """Point with validation."""
    x: float
    y: float

    def __post_init__(self) -> None:
        if self.x < 0 or self.y < 0:
            raise ValueError(f"Coordinates must be positive: ({self.x}, {self.y})")

With field defaults

from dataclasses import dataclass, field

@dataclass(frozen=True, slots=True)
class Config:
    """Configuration with defaults."""
    name: str
    enabled: bool = True
    options: tuple[str, ...] = field(default_factory=tuple)
    metadata: dict[str, str] = field(default_factory=dict)

Slots optimization

Why use slots

# Without slots: each instance has __dict__ for attributes
@dataclass
class RegularPoint:
    x: float
    y: float
# 120+ bytes per instance

# With slots: fixed attribute storage
@dataclass(slots=True)
class SlottedPoint:
    x: float
    y: float
# ~50 bytes per instance

Slots with inheritance

@dataclass(slots=True)
class Base:
    x: int

@dataclass(slots=True)
class Derived(Base):
    y: int
    # Python 3.10+ handles slots inheritance correctly

Immutability patterns

Frozen with copy modification

from dataclasses import dataclass, replace

@dataclass(frozen=True, slots=True)
class User:
    id: int
    name: str
    active: bool

# Modify by creating new instance
user = User(id=1, name="Alice", active=True)
updated = replace(user, active=False)

assert user.active is True
assert updated.active is False

Deeply immutable

from dataclasses import dataclass, field

@dataclass(frozen=True, slots=True)
class ImmutableConfig:
    """Deeply immutable config using tuples instead of lists."""
    name: str
    options: tuple[str, ...] = field(default_factory=tuple)

    @classmethod
    def from_list(cls, name: str, options: list[str]) -> 'ImmutableConfig':
        """Create from list, converting to tuple."""
        return cls(name=name, options=tuple(options))

Field patterns

Field with factory

from dataclasses import dataclass, field
from datetime import datetime

@dataclass(frozen=True, slots=True)
class Event:
    name: str
    timestamp: datetime = field(default_factory=datetime.now)
    tags: frozenset[str] = field(default_factory=frozenset)

Field with metadata

from dataclasses import dataclass, field

@dataclass(frozen=True, slots=True)
class FormField:
    name: str
    value: str = ""
    required: bool = field(default=False, metadata={'form': 'checkbox'})
    max_length: int = field(default=100, metadata={'form': 'hidden'})

Exclude from repr/compare

from dataclasses import dataclass, field

@dataclass(frozen=True, slots=True)
class CachedResult:
    key: str
    value: str
    # Cache metadata not part of equality or repr
    _cache_time: float = field(repr=False, compare=False, default=0.0)

Validation patterns

Post-init validation

from dataclasses import dataclass

@dataclass(frozen=True, slots=True)
class Range:
    start: int
    end: int

    def __post_init__(self) -> None:
        if self.start > self.end:
            raise ValueError(f"start ({self.start}) must be <= end ({self.end})")

Factory method validation

from dataclasses import dataclass

@dataclass(frozen=True, slots=True)
class Email:
    """Validated email address."""
    address: str

    def __post_init__(self) -> None:
        if '@' not in self.address:
            raise ValueError(f"Invalid email: {self.address}")

    @classmethod
    def parse(cls, value: str) -> 'Email':
        """Parse and validate email string."""
        return cls(address=value.strip().lower())

Comparison and ordering

Custom ordering

from dataclasses import dataclass
from functools import total_ordering

@total_ordering
@dataclass(frozen=True, slots=True, eq=True)
class Version:
    major: int
    minor: int
    patch: int

    def __lt__(self, other: 'Version') -> bool:
        return (self.major, self.minor, self.patch) < (other.major, other.minor, other.patch)

Hash for use in sets/dicts

@dataclass(frozen=True, slots=True)
class HashableItem:
    """Frozen dataclass is automatically hashable."""
    id: str
    name: str

# Can be used in sets and as dict keys
items = {HashableItem("1", "a"), HashableItem("2", "b")}
lookup = {HashableItem("1", "a"): "value"}

Pattern: Value object

from dataclasses import dataclass

@dataclass(frozen=True, slots=True)
class Money:
    """Immutable value object representing money."""
    amount: int  # In cents to avoid float issues
    currency: str

    def __post_init__(self) -> None:
        if self.amount < 0:
            raise ValueError("Amount cannot be negative")
        if len(self.currency) != 3:
            raise ValueError("Currency must be 3-letter code")

    def add(self, other: 'Money') -> 'Money':
        if self.currency != other.currency:
            raise ValueError("Cannot add different currencies")
        return Money(self.amount + other.amount, self.currency)

    def __str__(self) -> str:
        return f"{self.amount / 100:.2f} {self.currency}"

Checklist

  • Use
    frozen=True
    for immutable data
  • Use
    slots=True
    for memory efficiency
  • Validation in
    __post_init__
    or factory methods
  • Use
    tuple
    /
    frozenset
    for immutable collections
  • Use
    replace()
    for modifications
  • Document invariants

Additional resources: