# Python dataclass fields **Python dataclass fields** (from the `dataclasses` module) let you declare class attributes declaratively with type hints, default values, and factories. The `field()` function customizes how attributes behave: mutable defaults via factories, excluding from `__repr__`, marking as init-only, or running post-initialization logic with `__post_init__()`. Use dataclasses with fields to reduce boilerplate and make class structure explicit. ## Example This example shows dataclass field configuration and post-init hooks. ```python # run: python3 dataclass_fields.py # description: dataclass field configuration and post-init from dataclasses import dataclass, field from typing import List # Basic dataclass with defaults @dataclass class Point: x: float = 0.0 y: float = 0.0 p = Point(3.0, 4.0) print(f"Point: {p}") # Mutable default with factory @dataclass class Team: name: str members: List[str] = field(default_factory=list) team1 = Team("A") team1.members.append("Alice") team2 = Team("B") # has separate empty list print(f"Team 1: {team1}") print(f"Team 2: {team2}") # Field configuration @dataclass class Config: name: str password: str = field(repr=False) # hidden in repr debug: bool = field(default=False, init=False) # not in __init__ tags: List[str] = field(default_factory=list, compare=False) # excluded from comparisons config = Config("myapp", "secret123") print(f"Config: {config}") # password not shown print(f"Debug: {config.debug}") # Post-init processing @dataclass class Circle: radius: float area: float = field(init=False) # computed, not initialized def __post_init__(self): """Called after __init__ to compute derived attributes.""" import math self.area = math.pi * self.radius ** 2 circle = Circle(5.0) print(f"Circle: {circle}") print(f"Area: {circle.area:.2f}") # Field with default factory and post-init @dataclass class Database: host: str port: int = 5432 username: str = "admin" password: str = field(repr=False, default="") _connection: object = field(init=False, repr=False, default=None) def __post_init__(self): """Initialize connection after dataclass init.""" self._connection = f"postgres://{self.username}@{self.host}:{self.port}" db = Database("localhost") print(f"Database: {db}") print(f"Connection: {db._connection}") # Field with init=False and default_factory @dataclass class Cache: name: str _data: dict = field(init=False, default_factory=dict) _hits: int = field(init=False, default=0) def get(self, key): if key in self._data: self._hits += 1 return self._data.get(key) def set(self, key, value): self._data[key] = value cache = Cache("mycache") cache.set("key1", "value1") print(f"Cache: {cache}") print(f"Hits: {cache._hits}") ``` ## Common patterns **`field()` parameters**: - `default`: default value - `default_factory`: callable to generate default (for mutable types) - `init`: include in `__init__` (default True) - `repr`: include in `__repr__` (default True) - `compare`: include in comparisons (default True) - `hash`: include in `__hash__` (default is compare value) - `metadata`: arbitrary metadata dict for this field **Mutable defaults**: - Wrong: `items: list = []`: shared among all instances - Right: `items: list = field(default_factory=list)`: separate list per instance - Also works for `dict`, `set`, or any callable **Post-initialization**: - `__post_init__()` called after `__init__` completes - Use for computed attributes, validation, setup - Access all initialized attributes here **Init-only fields**: - `field(init=False)`: set after init - Useful for derived/computed attributes - Won't appear in `__init__` signature **Excluding from repr**: - `field(repr=False)`: hidden from `__repr__` output - Good for secrets, large objects, internal state **Comparison control**: - `field(compare=False)`: ignored in `__eq__`, `__lt__`, etc. - Useful for caches, id fields not affecting equality - By default all fields participate **Field ordering**: - Fields without defaults must come before fields with defaults - `field(default=..., ...)` counts as "has default" - Fields with `init=False` can go anywhere **Field metadata**: - `field(metadata={'description': '...'})`: store arbitrary info - Accessible via `fields()` function - Useful for generating docs, validators **Accessing field info**: - `from dataclasses import fields` - `fields(MyClass)`: returns tuple of Field objects - `Field.name`, `Field.type`, `Field.default`, etc. **When to use fields vs simple defaults**: - Use `field()` for any mutable default - Use `field()` for computed/derived attributes - Simple immutable defaults can use `attr: type = value` - Complex initialization use `__post_init__()`