@dataclass: классы без бойлерплейта
Декоратор @dataclass пишет за вас __init__, __repr__ и __eq__ по описанным полям класса. Разберём значения по умолчанию, field(default_factory) для списков, frozen=True для неизменяемых объектов и __post_init__ для производных полей.
Редакция Питоники
Класс-данные в чистом Python начинается с рутины: в __init__ по строке на каждое поле, затем __repr__, чтобы объект читался в отладке, затем __eq__, чтобы два одинаковых товара считались равными. Три метода на пять полей — экран бойлерплейта. Декоратор @dataclass берёт эту работу на себя: вы описываете только поля, он генерирует методы. Слева — что приходилось писать руками, справа — что достаточно теперь:
class ProductManual:
def __init__(self, name, price):
self.name = name
self.price = price
def __repr__(self):
return f"Product(name={self.name!r}, price={self.price!r})"
def __eq__(self, other):
if not isinstance(other, ProductManual):
return NotImplemented
return (self.name, self.price) == (other.name, other.price)
from dataclasses import dataclass
@dataclass
class Product:
name: str
price: int
a = Product("кружка", 350)
b = Product("кружка", 350)
print(a)
print(a == b)
Product(name='кружка', price=350) True
| Метод | Что генерирует @dataclass |
|---|---|
| __init__ | Поля становятся параметрами конструктора и записываются в self по порядку |
| __repr__ | Печатает имя класса и все поля: Product(name='кружка', price=350) |
| __eq__ | Сравнивает два объекта попарно по полям: одинаковые данные — равные объекты |
Обратите внимание на аннотации name: str и price: int: для @dataclass они обязательны — именно по их наличию декоратор отличает поля класса от обычных атрибутов. Значения по умолчанию описываются прямо в списке полей, как у обычных аргументов функции: после полей без значений могут идти только поля со значениями.
from dataclasses import dataclass
@dataclass
class Product:
name: str
price: int = 100
in_stock: bool = True
print(Product("степлер"))
print(Product("точилка", 80))
Product(name='степлер', price=100, in_stock=True) Product(name='точилка', price=80, in_stock=True)
from dataclasses import dataclass, field
@dataclass
class Order:
number: int
items: list = field(default_factory=list)
tags: list = field(default_factory=list)
def add(self, item):
self.items.append(item)
o1 = Order(1)
o2 = Order(2)
o1.add("ручка")
o1.tags.append("срочно")
print(o1)
print(o2)
Order(number=1, items=['ручка'], tags=['срочно']) Order(number=2, items=[], tags=[])
frozen=True и __post_init__
from dataclasses import dataclass
@dataclass(frozen=True)
class Point:
x: int
y: int
p = Point(2, 3)
print(p in {Point(2, 3), Point(4, 1)})
print(len({Point(2, 3), Point(2, 3)}))
try:
p.x = 10
except Exception as e:
print(type(e).__name__)
True 1 FrozenInstanceError
from dataclasses import dataclass, field
@dataclass
class Rectangle:
width: int
height: int
area: int = field(init=False)
def __post_init__(self):
self.area = self.width * self.height
r1 = Rectangle(3, 4)
r2 = Rectangle(5, 5)
print(r1)
print(r2.area)
Rectangle(width=3, height=4, area=12) 25
Сначала предскажи ответ в голове — это главный навык программиста.
from dataclasses import dataclass
@dataclass
class Cat:
name: str
age: int = 1
a = Cat("Барсик")
b = Cat("Барсик", 1)
print(a == b)
print(a)
from dataclasses import dataclass, field
@dataclass
class Team:
title: str
members: list = field(default_factory=list)
t = Team("альфа")
t.members.append("Аня")
print(t)
from dataclasses import dataclass
@dataclass
class Item:
price: int
count: int
total: int = 0
def __post_init__(self):
self.total = self.price * self.count
print(Item(50, 3).total)
1. Какие методы @dataclass генерирует по умолчанию?
2. Как правильно задать список как значение поля по умолчанию?
3. Что дают объекту параметры @dataclass(frozen=True)?
4. Когда вызывается метод __post_init__?
5. Сгенерированный dataclass-ом __eq__ сравнивает объекты…
6. Зачем в dataclass обязательны аннотации полей вроде name: str?
Опишите @dataclass Book с полями title и author (строки) и pages (int, по умолчанию 100). Создайте две одинаковые книги «Евгений Онегин» Пушкина на 384 страницы и выведите результат их сравнения через ==, затем напечатайте первую книгу.
Что такое @dataclass в Python простыми словами?
Это декоратор, который пишет за вас скучные методы класса-данных: по списку полей он генерирует конструктор __init__, печать __repr__ и сравнение __eq__. Вы описываете только поля с типами и значениями по умолчанию — как столбцы таблицы, а код методов Python собирает сам.
Урок 16: dataclassЧем dataclass лучше обычного класса?
Меньше кода и меньше ошибок: не нужно переписывать в __init__ каждое поле, не нужно вручную поддерживать __repr__ и __eq__ при добавлении поля — декоратор перегенерирует их сам из описания. Обычный класс остаётся выбором, когда конструктору нужна сложная логика, которую неудобно прятать в __post_init__.
Почему нельзя написать items: list = [] в dataclass?
Изменяемое значение по умолчанию сделало бы список общим для всех объектов — классическая ловушка из урока про атрибуты класса. Декоратор перехватывает её заранее и поднимает ValueError. Правильно: items: list = field(default_factory=list) — для каждого экземпляра вызывается list() и создаётся свой список.
Как сделать объект dataclass неизменяемым?
Передать декоратору параметр frozen=True: присваивание полю после создания поднимет FrozenInstanceError. Бонус — у frozen-класса работает __hash__, поэтому его экземпляры можно класть в множества и использовать как ключи словаря, что обычному dataclass с eq=True недоступно.
Понравился урок? Сошлитесь на него
Скопируйте готовую ссылку в формате HTML, Markdown или чистый адрес и вставьте в статью на Habr, VC, Telegram-канал или свой блог — так о проекте узнают новые читатели.
Что читать дальше
ООП · Урок 15
Итераторы: __iter__ и __next__
Каждый for в Python под капотом вызывает iter() и next(), а о конце данных сообщает исключение StopIteration. Научим свои классы работать в цикле: сначала вручную через __iter__ и __next__, потом коротким путём — генератором с yield.
ООП · Урок 17
Декораторы: функции над функциями
Декоратор — функция, которая принимает функцию и возвращает новую, добавляя поведение без правки исходного кода. Соберём декоратор с нуля: от замыкания и синтаксиса @ до functools.wraps, декораторов для методов и декораторов с параметрами.
Похожие уроки по темам
Подобраны автоматически по пересечению тем и ключевых слов.
pytest · Урок 2
Как pytest находит тесты: конвенции имён файлов и функций
pytest не читает мысли: тестом становится только то, что названо по конвенции — test_*.py, test_-функции, классы Test*. И адрес одного теста: файл::тест.
pytest test классытесты python pytest для начинающих
SQLAlchemy · Урок 3
Модели: декларативные таблицы
Таблица как класс Python: Base, Mapped-аннотации и mapped_column. Модель — это и код, и схема, и документация одновременно.
sqlalchemy таблица класс pythonsqlalchemy nullable default
ООП · Урок 1
Классы в Python: первый объект
Класс — чертёж, объект — экземпляр по этому чертежу. Создаём первый класс, вызываем методы и убеждаемся: в Python объектом является всё, включая функции.
классы pythonpython классы