Москва: проверенные б/у ноутбуки с гарантией и проверкой перед оплатой
+7 906 722-94-45

Главная → Статьи → Слоты классов: экономия памяти

Слоты классов: экономия памяти экземпляров

Слоты экземпляров: отказ от словаря атрибутов, фиксированный перечень полей, ограничения наследования и проверка фактической экономии.

Прямой ответ

Объявление слотов заменяет словарь атрибутов экземпляра фиксированным перечнем полей, поэтому память на экземпляр сокращается.

class Point:
    __slots__ = ("x", "y")

    def __init__(self, x, y):
        self.x = x
        self.y = y

p = Point(1, 2)
p.z = 3          # ошибка: атрибут не объявлен в слотах
Аспект Без слотов Со слотами
Хранение атрибутов Словарь экземпляра Фиксированные поля
Добавление атрибута Возможно Только объявленные
Память на экземпляр Больше Меньше

Слоты применяются, когда создаётся большое число экземпляров одного класса, поэтому экономия на каждом экземпляре накапливается. Об устройстве сборщика — в статье про garbage collector.

Ограничения слотов и наследование

Использование слотов накладывает ограничения, поэтому применение требует проверки совместимости.

Фиксированный перечень атрибутов. Атрибут, не объявленный в слотах, не добавляется, поэтому динамическое расширение экземпляра невозможно.

Наследование. Класс-наследник без собственных слотов получает словарь атрибутов, поэтому экономия теряется. Слоты родительского класса сохраняются.

Отсутствие значения по умолчанию. Поле слота не имеет значения до присваивания, поэтому обращение к неприсвоенному полю завершается ошибкой.

Практическое следствие: слоты применяются к классам с фиксированным набором полей, который не расширяется динамически.

Отдельный аспект — множественное наследование. Класс не может наследовать несколько родительских классов с непустыми слотами, поскольку расположение полей конфликтует.

Три признака того, что слоты не подходят.

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

Наследование от класса со словарем атрибутов. Экземпляр получает словарь, поэтому экономия памяти не достигается.

Малое число экземпляров. Экономия на каждом экземпляре не накапливается, поэтому усложнение кода не оправдано.

О поиске утечек памяти — в статье про утечки памяти; о дескрипторах как механизме управления доступом к полям — в статье про метаклассы и дескрипторы.

Проверка экономии выполняется сравнением размера экземпляра, поскольку фактическая экономия зависит от числа полей.

Слоты и слабые ссылки, сериализация

Использование слотов влияет на возможность создания слабой ссылки и на поведение при сериализации.

Слабые ссылки. Класс со слотами не поддерживает слабые ссылки по умолчанию, поэтому имя для слабой ссылки добавляется в перечень слотов явно. Отсутствие имени завершает создание слабой ссылки ошибкой.

Сериализация экземпляра. Экземпляр со слотами сериализуется по перечню полей, поэтому добавление поля в перечень изменяет формат. Экземпляр, сериализованный с прежним перечнем, восстанавливается с учётом соответствия полей.

Сравнение с классом без слотов. Экземпляр без слотов сериализуется через словарь атрибутов, поэтому динамически добавленные атрибуты также сохраняются.

Практическое следствие: перечень слотов определяет формат сериализации, поэтому изменение перечня влияет на восстановление ранее сохранённых экземпляров.

Сравнение с альтернативными способами сокращения памяти

Слоты — один из способов сокращения потребления памяти, поэтому выбор зависит от характера данных.

Именованный кортеж. Структура с фиксированными полями и неизменяемым содержимым занимает объём, сопоставимый со слотами. Изменение поля невозможно, поэтому применяется для данных, не изменяемых после создания.

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

Сокращение числа объектов. Часть данных представляется примитивными значениями вместо объектов, поэтому накладные расходы на экземпляр отсутствуют.

Практическое следствие: слоты применяются, когда требуется изменяемый объект с фиксированным набором полей. Для неизменяемых данных подходит именованный кортеж, для больших числовых наборов — массив. О поиске источников потребления — в статье про утечки памяти.

Практика: применение слотов

1. Определите фиксированный перечень полей. Слоты применяются к классам, набор полей которых известен при определении.

2. Объявляйте слоты в каждом классе иерархии. Класс-наследник без слотов получает словарь атрибутов, поэтому экономия теряется.

3. Проверяйте отсутствие динамического добавления атрибутов. Атрибут, не объявленный в слотах, не добавляется.

4. Измеряйте фактическую экономию. Сравнение размера экземпляра показывает выигрыш, а не предположение.

5. Учитывайте отсутствие значения по умолчанию. Поле не имеет значения до присваивания, поэтому обращение завершается ошибкой.

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

7. Проверяйте совместимость с наследованием. Множественное наследование от классов с непустыми слотами не допускается.

Рекомендации:

  1. Различайте экономию памяти и ускорение доступа. Слоты сокращают память, ускорение доступа к полю незначительно.
  2. Учитывайте влияние на отладку. Динамическое добавление атрибута завершается ошибкой, поэтому опечатка в имени поля обнаруживается сразу.
  3. Проверяйте поведение библиотек. Библиотека, добавляющая атрибуты экземпляру, несовместима со слотами.
  4. Фиксируйте перечень полей в документации. Перечень определяет контракт класса, поэтому изменение влияет на использование.
  5. Пересматривайте при изменении числа экземпляров. Экономия оправдана при большом числе экземпляров, а не при единичных объектах.

Практический сценарий: память на набор объектов превышала ожидаемую

Контекст: приложение, создававшее большое число однотипных объектов, потребляло заметно больше памяти, чем ожидалось.

  1. Диагностика. Каждый экземпляр содержал словарь атрибутов, который занимал объём, сопоставимый с самими данными. При большом числе экземпляров объём словарей составлял значительную долю потребления.
  2. Причина. Класс имел фиксированный набор полей, но словарь атрибутов создавался для каждого экземпляра. Экономия не применялась, поскольку слоты не были объявлены.
  3. Действия. (а) Объявили слоты с перечнем полей класса; (б) объявили слоты в классах-наследниках, чтобы словарь атрибутов не создавался; (в) измерили размер экземпляра до и после изменения; (г) проверили, что динамическое добавление атрибутов не используется в коде; (д) оценили сокращение общего потребления при фактическом числе объектов.
  4. Результат. Потребление памяти сократилось, поскольку словарь атрибутов не создаётся для каждого экземпляра.

Вывод: словарь атрибутов занимает объём, сопоставимый с данными, поэтому при большом числе экземпляров потребление растёт. Слоты исключают создание словаря и сокращают память на экземпляр.

FAQ

Что дают слоты? Заменяют словарь атрибутов фиксированными полями, поэтому память на экземпляр сокращается. Добавление атрибута, не объявленного в слотах, завершается ошибкой.

Нужно ли объявлять слоты в классах-наследниках? Да, класс-наследник без собственных слотов получает словарь атрибутов, поэтому экономия теряется. Слоты родительского класса сохраняются.

Имеет ли поле слота значение по умолчанию? Нет, поле не имеет значения до присваивания, поэтому обращение к неприсвоенному полю завершается ошибкой.

Когда слоты не подходят? При динамическом добавлении атрибутов, при малом числе экземпляров и при наследовании от класса со словарем атрибутов.

Источники: Python datamodel slots, sys.getsizeof, gc — ссылки в шапке.