Table of Contents

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:

Mutable defaults:

Post-initialization:

Init-only fields:

Excluding from repr:

Comparison control:

Field ordering:

Field metadata:

Accessing field info:

When to use fields vs simple defaults: