Site Tools


python-dataclass-fields

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.

# 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__()
python-dataclass-fields.md · Last modified: by 127.0.0.1