🎉 75% of content is free forever — Unlock Premium from $10/mo →
CW
Search courses…
💼 Servicesℹ️ About✉️ ContactView Pricing Plansfrom $10

Python Dataclasses — Modern Class Syntax

Python AdvancedDataclasses🟢 Free Lesson

Advertisement

Python Dataclasses — Modern Class Syntax

Dataclasses reduce boilerplate for classes that primarily store data. They auto-generate __init__, __repr__, __eq__, and more using a simple decorator.

Learning Objectives

  • Create dataclasses with @dataclass decorator
  • Use field() for custom defaults and options
  • Create frozen (immutable) dataclasses with slots
  • Use post_init for computed fields and validation
  • Compare dataclasses with NamedTuple, attrs, and regular classes

Basic Dataclass

The @dataclass decorator automatically generates special methods:

from dataclasses import dataclass

@dataclass
class Point:
    x: float
    y: float

p = Point(3.0, 4.0)
print(p)            # Point(x=3.0, y=4.0)
print(p.x, p.y)    # 3.0 4.0
print(p == Point(3.0, 4.0))  # True — auto-generated __eq__

What @dataclass generates automatically:

MethodGeneratedCustomizable
__init__YesYes
__repr__YesYes
__eq__YesYes
__hash__No (frozen=True)Yes
__lt__NoYes

field() Options

Use field() when you need mutable defaults or custom behavior:

from dataclasses import dataclass, field
from typing import List, Dict

@dataclass
class Student:
    name: str
    age: int
    grades: List[float] = field(default_factory=list)
    gpa: float = field(init=False)
    tags: Dict[str, str] = field(default_factory=dict)

    def __post_init__(self):
        if self.grades:
            self.gpa = sum(self.grades) / len(self.grades)
        else:
            self.gpa = 0.0

s = Student("Alice", 20, [3.5, 4.0, 3.8])
print(s.gpa)  # 3.766...
print(s.tags)  # {}

Field options reference:

OptionDescription
defaultDefault value for the field
default_factoryFactory function for mutable defaults
initInclude field in __init__ (default: True)
reprInclude field in __repr__ (default: True)
compareInclude field in __eq__ and __lt__ (default: True)
hashInclude field in __hash__
metadataDict of user-defined data

Frozen Dataclass (Immutable)

Create immutable instances that can be used as dict keys or set elements:

@dataclass(frozen=True)
class Config:
    host: str = "localhost"
    port: int = 8080
    debug: bool = False

config = Config()
# config.port = 9000  # AttributeError: frozen

# Hashable — can be dict key or set element
configs = {Config(): "default", Config("0.0.0.0", 9000): "custom"}
print(configs[Config()])  # "default"

Slots for Memory Efficiency

Use slots=True to reduce memory usage:

@dataclass(slots=True)
class Vector:
    x: float
    y: float

v = Vector(1.0, 2.0)
print(v.x, v.y)

# Without slots, each instance has a __dict__ (~400 bytes overhead)
# With slots, memory is reduced by ~40% for many instances

post_init for Computed Fields

Use __post_init__ to compute derived values or validate data:

from dataclasses import dataclass, field
from datetime import datetime

@dataclass
class Event:
    name: str
    start: datetime
    end: datetime
    duration_hours: float = field(init=False)

    def __post_init__(self):
        if self.end <= self.start:
            raise ValueError("End must be after start")
        delta = self.end - self.start
        self.duration_hours = delta.total_seconds() / 3600

event = Event("Meeting", datetime(2024, 1, 1, 9), datetime(2024, 1, 1, 11))
print(event.duration_hours)  # 2.0

Inheritance

Dataclasses support inheritance cleanly:

@dataclass
class Animal:
    name: str
    species: str

@dataclass
class Pet(Animal):
    owner: str
    vaccinated: bool = True

pet = Pet("Rex", "Dog", "Alice")
print(pet)  # Pet(name='Rex', species='Dog', owner='Alice', vaccinated=True)

Warning: Inheritance with frozen=True can be tricky — child classes may need special handling.


Comparison Table

FeatureDataclassNamedTupleattrsRegular Class
BoilerplateLowLowMediumHigh
MutableYesNoYesYes
HashableOptionalAlwaysOptionalNo
Type hintsRequiredRequiredRequiredOptional
InheritanceYesLimitedYesYes
PerformanceGoodBestGoodGood

Real-World Example: API Response Model

from dataclasses import dataclass, field
from typing import List, Optional
from datetime import datetime

@dataclass
class APIResponse:
    status: int
    data: List[dict]
    message: str = "OK"
    timestamp: datetime = field(default_factory=datetime.now)
    metadata: dict = field(default_factory=dict)

    @property
    def is_success(self) -> bool:
        return 200 <= self.status < 300

    def add_metadata(self, key: str, value):
        self.metadata[key] = value

response = APIResponse(200, [{"id": 1}], "Users retrieved")
response.add_metadata("page", 1)
print(response.is_success)  # True

Common Mistakes

MistakeProblemSolution
Using mutable defaultgrades: List = []Use field(default_factory=list)
Forgetting init=FalseComputed field in __init__Set init=False for derived fields
Mixing frozen and mutablefrozen=True with mutable fieldsUse field(hash=False) or restructure
OvercomplicatingUsing dataclass for behavior-heavy classesUse regular class if methods dominate

Key Takeaways

  1. @dataclass auto-generates __init__, __repr__, __eq__ — reducing boilerplate by ~50%
  2. Always use field(default_factory=...) for mutable defaults (lists, dicts, sets)
  3. frozen=True creates immutable, hashable instances — perfect for config objects
  4. __post_init__ enables computed fields and validation without extra __init__ code
  5. Use slots=True for memory-efficient instances when storing many objects
  6. Prefer dataclasses over NamedTuple for mutable data containers

Premium Content

Python Dataclasses — Modern Class Syntax

Unlock this lesson and 900+ advanced tutorials with a Premium plan.

🎯End-to-end Projects
💼Interview Prep
📜Certificates
🤝Community Access

Already a member? Log in

Need Expert Python Help?

Get personalized tutoring, project support, or professional consulting.

Advertisement