Что такое __slots__ в Python?

Ответ

__slots__ — это механизм в Python для оптимизации памяти, ограничивающий набор атрибутов экземпляра класса. Он заменяет динамический словарь __dict__ фиксированным набором атрибутов, что уменьшает потребление памяти и ускоряет доступ к атрибутам.

Пример:

class User:
    __slots__ = ['name', 'age']  # Фиксированные атрибуты

    def __init__(self, name, age):
        self.name = name
        self.age = age

user = User("Alice", 30)
print(user.name)  # Alice
# user.email = "alice@example.com"  # Вызовет AttributeError (атрибут не в __slots__)

Плюсы:

  • Экономия памяти (особенно полезно для создания множества экземпляров).
  • Более быстрый доступ к атрибутам.

Минусы:

  • Нельзя динамически добавлять новые атрибуты.
  • Наследование требует аккуратности: если родительский класс имеет __slots__, то у класса-потомка тоже должен быть определен __slots__ (он может быть пустым или расширять родительский).

Ответ 18+ 🔞

Э, слушай, а вот про эту штуку __slots__ — это же просто песня, а не оптимизация. Представь себе обычный класс в Python. По умолчанию у каждого его экземпляра под капотом есть словарик __dict__, куда он складывает все свои атрибуты. Это удобно, можно на лету что угодно прилепить: obj.new_field = "ой, а что это?". Но за это удобство, чувак, платится память. Каждый такой словарик — это овердохуища накладных расходов, особенно если ты создаёшь их миллионы, как каких-нибудь точек в графике или пользователей в базе.

А теперь смотри, как __slots__ всё ломает в хорошем смысле. Ты просто говоришь классу: «Браток, хватит быть таким распиздяем и таскать с собой этот жирный словарь. Вот тебе список разрешённых имён атрибутов. И ни шагу в сторону!»

class СуровыйМужик:
    __slots__ = ['имя', 'сила']  # Всё, точка. Больше ничего нельзя.

    def __init__(self, имя, сила):
        self.имя = имя
        self.сила = сила

герой = СуровыйМужик("Илья", 100500)
print(герой.сила)  # 100500
# герой.ловкость = 50  # А вот это уже вызовет AttributeError! Пиздец и кирдык.

Что хорошего, ёпта?

  • Памяти жрёт как маньяк-недомерок. Серьёзно, экономия может быть в разы, потому что вместо словаря для каждого объекта создаётся компактный дескриптор. Если ты делаешь какую-нибудь свою структуру данных и плодишь её экземпляры как сумасшедший — это твой выбор.
  • Доступ к атрибутам становится шустрее. Нет лишних хэш-таблиц, всё по фиксированным смещениям. Не космическая разница, но приятно.

А где подвох, хитрая жопа?

  • Динамические атрибуты — нахуй. Захотел на лету добавить объект.новое_поле = "сюрприз" — получи AttributeError прямо в ебало. Доверия к такому объекту — ноль ебать, он жёстко зафиксирован.
  • С наследованием надо головой думать. Если у родителя есть __slots__, то у наследника тоже надо свой объявлять. Можно просто пустой [] оставить, если ничего своего не нужно, или добавить новые поля. Главное — не забыть, а то будет тебе ёперный театр с ошибками. И да, если в цепочке наследования хоть один класс без __slots__, то у экземпляров всё равно появится __dict__, и вся магия экономии памяти может накрыться медным тазом.

Короче, инструмент узкоспециальный. Не лепи его везде, а только там, где реально нужно с памятью бороться и ты на сто пятьсот процентов уверен в структуре своих объектов. А то загонишь себя в угол, как этот самый СуровыйМужик без возможности прокачать ловкость.