Syllabus Lesson 53 of 239 · Object-Oriented Python
Object-Oriented Python

Less Boilerplate With @dataclass

Writing __init__, __repr__, and __eq__ by hand for a simple data-holding class gets repetitive. The @dataclass decorator from the standard library writes them for you. You just list the fields with type hints.

from dataclasses import dataclass

@dataclass
class Point:
    x: int
    y: int

p = Point(1, 2)
print(p)            # Point(x=1, y=2)   (free __repr__)
print(p == Point(1, 2))   # True          (free __eq__)
print(p.x, p.y)     # 1 2

With three lines you got an __init__ that takes x and y, a readable __repr__, and value-based __eq__. No self.x = x chores.

Defaults and methods

Fields can have defaults, and you can still add your own methods like any class:

from dataclasses import dataclass

@dataclass
class Item:
    name: str
    price: float
    qty: int = 1

    def total(self):
        return self.price * self.qty

i = Item("Pen", 2.5, 4)
print(i.total())   # 10.0

One caution on defaults: a mutable default such as tags: list = [] is not allowed and raises ValueError: mutable default ... is not allowed. It is the same shared-default trap functions have, so dataclasses block it up front. Reach for field(default_factory=list) instead, which hands every instance its own fresh list.

from dataclasses import dataclass, field

@dataclass
class Cart:
    items: list = field(default_factory=list)

print(Cart().items)   # []

The type hints (x: int) are not enforced by Python, they just document intent. The real work is the boilerplate the decorator generates.

Your turn

Use @dataclass to define a class Product with fields name (str), price (float), and quantity (int) defaulting to 1. Add a method subtotal(self) returning price * quantity. Remember to import dataclass from dataclasses.

Spotted a problem in this lesson? Report it

Code · runs in your browser
Output