Skip to content

Dataclass Inheritance

Dataclasses support inheritance with automatic handling of parent and child fields. Understanding field ordering rules is important.


Basic Inheritance

from dataclasses import dataclass

@dataclass
class Animal:
    name: str
    age: int

@dataclass
class Dog(Animal):
    breed: str

dog = Dog("Buddy", 3, "Golden Retriever")
print(dog)  # Dog(name='Buddy', age=3, breed='Golden Retriever')

Field Ordering in Inheritance

from dataclasses import dataclass, field

@dataclass
class Base:
    x: int
    y: int = 10

# Fields with defaults must come after fields without defaults
@dataclass
class Derived(Base):
    z: int = 20  # Must have default since parent has default field

derived = Derived(1)
print(derived)  # Derived(x=1, y=10, z=20)

# This would fail: fields without defaults after those with defaults
# @dataclass
# class BadDerived(Base):
#     z: int  # Error: non-default field after default field

Overriding Parent Fields

from dataclasses import dataclass, field

@dataclass
class Vehicle:
    wheels: int = 4
    color: str = "white"

@dataclass
class Car(Vehicle):
    # Override with different default
    wheels: int = 4  # Explicitly set
    color: str = "silver"  # Different default
    doors: int = 4

car = Car()
print(car)  # Car(wheels=4, color='silver', doors=4)

Multiple Inheritance

from dataclasses import dataclass

@dataclass
class TimestampMixin:
    created_at: str = "2024-01-01"

@dataclass
class NameMixin:
    name: str

@dataclass
class Document(NameMixin, TimestampMixin):
    content: str = ""

doc = Document(name="Report", content="Summary")
print(doc)  # Document(name='Report', created_at='2024-01-01', content='Summary')

Initialization Behavior

from dataclasses import dataclass

@dataclass
class Parent:
    x: int

    def __post_init__(self):
        print(f"Parent init: x={self.x}")

@dataclass
class Child(Parent):
    y: int = 0

    def __post_init__(self):
        super().__post_init__()  # Call parent
        print(f"Child init: y={self.y}")

child = Child(1, 2)
# Output:
# Parent init: x=1
# Child init: y=2

Inheritance with Mutable Defaults

from dataclasses import dataclass, field
from typing import List

@dataclass
class BaseCollection:
    items: List[str] = field(default_factory=list)

@dataclass
class ExtendedCollection(BaseCollection):
    metadata: dict = field(default_factory=dict)

col1 = ExtendedCollection()
col2 = ExtendedCollection()

col1.items.append("item1")
col1.metadata['type'] = 'test'

print(col1)  # ExtendedCollection(items=['item1'], metadata={'type': 'test'})
print(col2)  # ExtendedCollection(items=[], metadata={})  # Independent

Best Practices

  • Keep parent dataclasses simple
  • Place required fields in parent, optional in child
  • Call super().__post_init__() when overriding
  • Consider composition over inheritance for complex cases