dataclasses и __slots__

Как dataclass избавляет от шаблонного кода и зачем __slots__ экономит память.

dataclass — декоратор, который по аннотациям полей автоматически генерирует __init__, __repr__, __eq__ и, по желанию, методы сравнения.

Проблема: много шаблонного кода

Класс-«контейнер данных» в обычном Python требует руками написать конструктор, читаемое представление и сравнение. Это утомительно и легко ошибиться. @dataclass делает всё это за вас.

from dataclasses import dataclass

@dataclass
class Point:
    x: int = 0
    y: int = 0
    def dist(self):
        return (self.x ** 2 + self.y ** 2) ** 0.5

p1 = Point(3, 4)
p2 = Point(3, 4)

print(p1)                  # авто __repr__
print("Равны:", p1 == p2)  # авто __eq__ сравнивает по полям
print("Расстояние:", p1.dist())

Вывод:

Point(x=3, y=4)
Равны: True
Расстояние: 5.0

Мы не писали ни __init__, ни __repr__, ни __eq__ — декоратор сгенерировал их по аннотациям x: int и y: int. Сравнение p1 == p2 идёт по значениям полей, а не по идентичности объектов.

Сравнение и порядок

Параметр order=True добавляет методы <, <=, >, >= — они сравнивают объекты как кортежи их полей, по порядку объявления.

from dataclasses import dataclass

@dataclass(order=True)
class Version:
    major: int
    minor: int

v1 = Version(1, 2)
v2 = Version(1, 5)
print("v1 < v2:", v1 < v2)
print("Отсортировано:", sorted([Version(2, 0), Version(1, 9), Version(1, 1)]))

Вывод:

v1 < v2: True
Отсортировано: [Version(major=1, minor=1), Version(major=1, minor=9), Version(major=2, minor=0)]

__slots__: экономия памяти

Обычный объект хранит атрибуты в словаре __dict__, что гибко, но затратно по памяти. Объявив __slots__ с фиксированным списком атрибутов, вы убираете этот словарь: память экономится, а добавить «левый» атрибут уже нельзя.

class WithSlots:
    __slots__ = ("a", "b")     # разрешены только эти атрибуты
    def __init__(self, a, b):
        self.a = a
        self.b = b

w = WithSlots(1, 2)
print("a, b:", w.a, w.b)

try:
    w.c = 3      # атрибута c нет в __slots__
except AttributeError as e:
    print("Нельзя добавить c:", type(e).__name__)

Вывод:

a, b: 1 2
Нельзя добавить c: AttributeError

Запрет на новые атрибуты — это и плюс (ловит опечатки в именах полей), и ограничение. __slots__ особенно полезен, когда в программе создаются миллионы мелких объектов: экономия на словарях ощутима.

Итог

  • @dataclass автогенерирует __init__, __repr__, __eq__ по аннотациям полей.
  • order=True добавляет операторы сравнения по кортежу полей.
  • __slots__ убирает __dict__: экономит память и запрещает посторонние атрибуты.
Проверьте себя
1. Что автоматически генерирует декоратор @dataclass?
AТолько __init__
B__init__, __repr__ и __eq__ по аннотациям полей
CМетоды для работы с сетью
DСлоты __slots__
2. Как @dataclass с __eq__ сравнивает два объекта?
AПо идентичности (один и тот же объект в памяти)
BПо значениям всех полей
CПо именам классов
DВсегда возвращает False
3. Что даёт объявление __slots__ в классе?
AУскоряет импорт модуля
BУбирает __dict__: экономит память и запрещает атрибуты вне списка
CДелает класс потокобезопасным
DАвтоматически добавляет __repr__
Поддержать проект