Что делает __slots__ технически
По умолчанию у экземпляра пользовательского класса есть словарь __dict__, где хранятся все его атрибуты. Это даёт гибкость, но стоит памяти и косвенного обращения через хеш‑таблицу.
Если в классе объявить __slots__, интерпретатор:
class Point:
__slots__ = ("x", "y")
def __init__(self, x, y):
self.x = x
self.y = y
Экземпляр Point будет занимать меньше памяти, чем аналогичный класс без __slots__, и не позволит динамически добавить новый атрибут (p.z = ... приведёт к AttributeError).
Влияние на производительность
Память: для большого числа однотипных объектов экономия может быть существенной (десятки процентов на экземпляр), особенно если в объекте всего несколько полей.
Скорость: доступ к атрибуту через фиксированный offset обычно немного быстрее, чем через словарь; выигрыш небольшой, но на горячих путях может быть заметен.
Ограничения и подводные камни
нельзя добавлять произвольные атрибуты, не указанные в __slots__ (если не включить "__dict__" в список слотов);
слоты и наследование: при сложной иерархии важно аккуратно проектировать __slots__ в базах и наследниках, объединять их и помнить, что слоты не наследуются «автоматически» как ожидают многие;
если нужны weakref’ы, нужно явно добавить "__weakref__" в __slots__;
в обычном бизнес‑коде для десятков объектов выгода зачастую не оправдывает усложнение модели классов.
Когда __slots__ уместен
массовые структуры: миллионы однотипных объектов (DTO, ноды AST, элементы графа, внутренние структуры в высоконагруженном коде);
слои, где важно жёстко фиксировать API объекта и не допускать динамических атрибутов (раннее выявление опечаток/ошибок);
в сочетании с dataclasses (slots=True), где генерация кода и слотов берётся на себя стандартной библиотекой.
Формулировка уровня senior
__slots__ — это способ сказать интерпретатору: «экземпляры этого класса имеют фиксированный набор полей, словарь атрибутов им не нужен». Это уменьшает память и иногда ускоряет доступ к атрибутам, но усложняет наследование и лишает гибкости динамических атрибутов. В продакшене я применяю __slots__ точечно — в горячих местах и массовых структурах, чаще через @dataclass(slots=True), чем руками.