Python dataclass slots: меньше памяти на объект
@dataclass(slots=True) добавляет классу __slots__ и убирает у каждого экземпляра словарь __dict__. Для тысяч небольших объектов это экономит память и немного ускоряет доступ к полям. Параметр появился в Python 3.10; на 3.9 и раньше __slots__ приходилось описывать вручную.
Создать dataclass без словаря
from dataclasses import dataclass
@dataclass(slots=True)
class Point:
x: int
y: int
point = Point(10, 20)
print(point) # Point(x=10, y=20)
print(hasattr(point, "__dict__")) # False
try:
point.z = 30
except AttributeError as exc:
print(type(exc).__name__) # AttributeErrorЭкземпляр хранит только объявленные поля, поэтому новое поле нельзя добавить на лету. Это не только экономит память, но и ловит опечатки в именах атрибутов. Если объекту нужны динамические свойства, slots=True не подходит — используйте обычный dataclass или словарь.
Сравнить размер и наследование
from dataclasses import dataclass
import sys
@dataclass
class Regular:
x: int
y: int
@dataclass(slots=True)
class Slotted:
x: int
y: int
print(hasattr(Regular(1, 2), "__dict__")) # True
print(hasattr(Slotted(1, 2), "__dict__")) # False
print(sys.getsizeof(Regular(1, 2)) >= sys.getsizeof(Slotted(1, 2))) # Truesys.getsizeof() показывает размер самого объекта без содержимого словаря, поэтому реальная экономия выше. Для точного замера используйте tracemalloc или pympler.asizeof.
Если базовый класс не имеет __slots__, экземпляр всё равно получит __dict__, и часть экономии потеряется. В иерархии объявляйте слоты на всех уровнях. Если поле уже есть в __slots__ базового класса, начиная с Python 3.11 dataclass не добавит его повторно, поэтому не используйте __slots__ для перечисления полей — берите dataclasses.fields(). Для слабых ссылок добавьте weakref_slot=True (Python 3.11+), иначе weakref.ref(point) не сработает.
slots=True возвращает новый класс, а не меняет исходный. Документация предупреждает, что передача параметров в __init_subclass__ базового класса в этом режиме может дать TypeError; метаклассы и библиотеки, завязанные на __dict__, тоже стоит проверить тестами. Если нужна максимальная совместимость, объявите __slots__ вручную или оставьте обычный dataclass.
Документация: dataclasses, slots.