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__: экономит память и запрещает посторонние атрибуты.