Back to Blog

Dataclasses (@dataclass, field, frozen, post_init) - Python Tutorial #30

Sandy LaneSandy Lane

Video: Dataclasses (@dataclass, field, frozen, post_init) - Python Tutorial #30 by Taught by Celeste AI - AI Coding Coach

Watch full page →

Python Dataclasses: Simplify Your Classes with @dataclass, field, frozen, and post_init

Python's dataclasses module provides a decorator that automatically generates special methods like __init__, __repr__, and __eq__ for classes, reducing boilerplate code. This tutorial covers how to use @dataclass with type annotations, customize fields with the field() function, create immutable dataclasses using frozen=True, and run additional initialization logic with the __post_init__ method.

Code

from dataclasses import dataclass, field

@dataclass
class Product:
  name: str
  price: float
  tags: list[str] = field(default_factory=list)  # Use default_factory for mutable defaults
  discount: float = 0.0
  final_price: float = field(init=False)  # Computed field, excluded from __init__

  def __post_init__(self):
    # Calculate final price after discount once object is created
    self.final_price = self.price * (1 - self.discount)

@dataclass(frozen=True)  # Makes instances immutable
class Point:
  x: int
  y: int

# Example usage:
p = Product(name="Coffee Maker", price=99.99, discount=0.1)
print(p)  # Product(name='Coffee Maker', price=99.99, tags=[], discount=0.1, final_price=89.991)

pt = Point(3, 4)
print(pt)  # Point(x=3, y=4)
# pt.x = 10  # This would raise dataclasses.FrozenInstanceError because Point is frozen

Key Points

  • @dataclass automatically generates __init__, __repr__, and __eq__ methods based on class fields.
  • Use field(default_factory=...) for mutable default values like lists to avoid shared references.
  • Set frozen=True in @dataclass to make instances immutable and hashable.
  • Define a __post_init__ method to perform additional initialization or compute derived fields after __init__.
  • Fields with init=False are excluded from the generated __init__ and can be set inside __post_init__.