ПрограммированиеBackend Python разработчик

Что такое __slots__ в Python классах, зачем они нужны и каковы их ограничения?

Проходите собеседования с ИИ помощником Hintsage

Ответ.

__slots__ — специальный атрибут класса, который ограничивает набор допустимых атрибутов экземпляра, экономит память и ускоряет доступ к атрибутам. Использование __slots__ особенно актуально для большого числа однотипных объектов.

История вопроса:

Появление __slots__ связано с тем, что по умолчанию у каждого экземпляра Python-объекта есть словарь атрибутов (__dict__), что удобно, но затратно по памяти. Для миллиона объектов с небольшим набором полей возникает значительный overhead.

Проблема:

Экземпляры обычного класса могут динамически расширяться, что удобно, но неэффективно. Применение __slots__ ограничивает динамическое добавление новых атрибутов, убирает экземплярный словарь атрибутов, экономит память и ускоряет доступ.

Решение:

Описывайте список разрешённых полей в атрибуте __slots__:

class Point: __slots__ = ('x', 'y') def __init__(self, x, y): self.x = x self.y = y p = Point(1, 2) p.z = 3 # AttributeError: 'Point' object has no attribute 'z'

Ключевые особенности:

  • Экономит память за счет отсутствия dict и weakref по умолчанию.
  • Определяет строгий набор разрешённых полей.
  • Позволяет ускорить доступ к атрибутам благодаря предвыделенным слотам (не требует поиска в dict).

Вопросы с подвохом.

Можно ли создать экземпляр класса с slots и потом добавить атрибут не из списка?

Нет. При попытке добавить атрибут отсутствующий в списке slots выбросится AttributeError. Это накладывает ограничения на расширяемость объекта.

Можно ли наследовать класс с slots и добавить новые поля?

Да, но каждый наследник должен объявлять свои slots. При этом родительские и текущие slots объединяются. Однако, если не объявить slots в наследнике, у потомка снова появится dict!

Работает ли slots для неизменяемых (immutable) типов?

Да, но надо реализовывать дополнительные меры, чтобы сделать слотовый объект неизменяемым (например, через property без сеттера).

Типовые ошибки и анти-паттерны

  • Не объявлять slots в дочернем классе, ломая памятьную эффективность.
  • Ожидать работу slots в присутствии multiple inheritance — могут возникать конфликты и ошибки.
  • Попытка сериализовать объекты со слотами стандартными средствами иногда затруднена (например pickle требует dict).

Пример из жизни

Негативный кейс

Обычный класс для точки:

class Point: def __init__(self, x, y): self.x = x self.y = y points = [Point(i, i) for i in range(1_000_000)]

Плюсы:

  • Гибко — можно добавлять любые атрибуты.
  • Понятно для новичков.

Минусы:

  • tasks manager показывает заметно завышенное потребление памяти (до +20-30%).

Позитивный кейс

Аналогичный класс, но со слотами:

class Point: __slots__ = ('x', 'y') def __init__(self, x, y): self.x = x self.y = y points = [Point(i, i) for i in range(1_000_000)]

Плюсы:

  • До 30% экономии памяти на миллионах объектов.
  • Быстрее доступ к полям.

Минусы:

  • Объекты становятся негибкими: нельзя добавить новые атрибуты на лету.