Короткий ответ
@dataclass автоматически генерирует __init__, __repr__ и __eq__ на основе аннотированных полей класса; поведение можно настроить параметрами декоратора — eq=False отключает сравнение по значению, repr=False отключает автогенерацию строкового представления, а frozen=True делает экземпляры неизменяемыми после создания (присваивание полям вызывает исключение).
Как это работает подробнее
По умолчанию @dataclass читает аннотации типов на уровне класса и генерирует __init__, который присваивает каждому аргументу соответствующее поле, __repr__, печатающий все поля в читаемом виде, и __eq__, сравнивающий объекты как tuple из значений полей в порядке объявления. frozen=True переопределяет __setattr__/__delattr__, чтобы бросать FrozenInstanceError на попытку изменить поле после __init__, что также делает экземпляр потенциально хешируемым (при согласованных условиях), в отличие от обычного изменяемого dataclass.
Пример кода
from dataclasses import dataclass, field
@dataclass(frozen=True)
class Money:
amount: float
currency: str = "RUB"
m1 = Money(100.0)
m2 = Money(100.0)
print(m1 == m2) # True — сравнение по значению полей
print(m1) # Money(amount=100.0, currency='RUB')
try:
m1.amount = 200.0 # FrozenInstanceError: экземпляр неизменяем
except Exception as e:
print("Ошибка:", type(e).__name__)
@dataclass
class Basket:
items: list = field(default_factory=list) # безопасный default для listЧто использовать на практике
- Практика. dataclass обычно выбирают для DTO, объектов-значений и простых структур конфигурации, где не нужна сложная бизнес-логика, но важны автоматическое сравнение по значению и читаемый
reprдля логов/отладки. Поля с изменяемым значением по умолчанию (list, dict) нельзя указывать напрямую — для них используютfield(default_factory=list), чтобы повторить защиту от общего mutable default, о которой говорилось для обычных функций.
Подводные камни
- Риски. Указывают
field: list = []вместоfield: list = field(default_factory=list)и получают тот же классический баг общего изменяемого default между экземплярами, что и в обычных функциях. Также ожидают, чтоfrozen=Trueделает неизменяемыми и вложенные объекты (списки/словари внутри полей) — это не так, frozen запрещает только переприсваивание самих полей объекта.