СДЕЛАЙТЕ СВОИ УРОКИ ЕЩЁ ЭФФЕКТИВНЕЕ, А ЖИЗНЬ СВОБОДНЕЕ

Благодаря готовым учебным материалам для работы в классе и дистанционно

Скидки до 50 % на комплекты
только до

Готовые ключевые этапы урока всегда будут у вас под рукой

Организационный момент

Проверка знаний

Объяснение материала

Закрепление изученного

Итоги урока

Методическая разработка «__slots__ в Python»

Категория: Информатика

Нажмите, чтобы узнать подробности

Просмотр содержимого документа
«Методическая разработка «__slots__ в Python»»

МУНИЦИПАЛЬНОЕ АВТОНОМНОЕ ОБРАЗОВАТЕЛЬНОЕ УЧРЕЖДЕНИЕ ДОПОЛНИТЕЛЬНОГО ОБРАЗОВАНИЯ

«ЦЕНТР ДЕТСКОГО ТЕХНИЧЕСКОГО ТВОРЧЕСТВА»













Методическая разработка

«__slots__ в Python»

к дополнительной общеобразовательной

общеразвивающей программе

технической направленности

«Программирование на Python»



Возраст детей: 10-17 лет









Автор: Костычев Вадим Александрович











г. Заречный Пензенской области

2026 г.

Данный материал предназначен для преподавателей информатики, программирования и студентов (учащихся), изучающих язык программирования Python. Он может быть использован как на лекциях, так и при проведении практических занятий или самостоятельной работе.


Цель: сформировать у учащихся понимание механизма __slots__ как инструмента оптимизации памяти и контроля над атрибутами экземпляров класса, а также научить применять его в практических задачах.


Задачи:

  1. Объяснить, как Python хранит атрибуты объектов по умолчанию (через __dict__).

  2. Показать проблемы, связанные с использованием __dict__ (избыточное потребление памяти, возможность добавления произвольных атрибутов).

  3. Ввести механизм __slots__ как альтернативу __dict__.

  4. Научить объявлять и использовать __slots__ в классах.

  5. Разобрать ограничения и особенности применения __slots__.


Как Python хранит атрибуты по умолчанию?


По умолчанию каждый экземпляр класса в Python имеет специальный атрибут __dict__ — это словарь, в котором хранятся все пользовательские атрибуты объекта.


class Point:

def __init__(self, x, y):

self.x = x

self.y = y


p = Point(1, 2)

print(p.__dict__) # {'x': 1, 'y': 2}


Это удобно: можно динамически добавлять новые атрибуты:


p.z = 3

print(p.__dict__) # {'x': 1, 'y': 2, 'z': 3}


Но есть два недостатка:

1. Избыточное потребление памяти: словарь занимает много места, особенно если объектов много.

2. Отсутствие контроля: можно случайно создать атрибут с опечаткой (p.x_coor вместо p.x_coord), и ошибка не будет замечена.


Что такое __slots__?


Механизм __slots__ позволяет заменить __dict__ на фиксированный набор атрибутов, заданных при определении класса.


Синтаксис:


class ClassName:

__slots__ = ('attr1', 'attr2', ...)



Пример:


class Point:

__slots__ = ('x', 'y')

def __init__(self, x, y):

self.x = x

self.y = y


p = Point(1, 2)

print(p.x, p.y) # 1 2


Теперь объект не имеет __dict__:


print(p.__dict__) # AttributeError: 'Point' object has no attribute '__dict__'


И нельзя добавить новый атрибут:


p.z = 3 # AttributeError: 'Point' object has no attribute 'z'


Преимущества __slots__


  1. Экономия памяти

Объекты со __slots__ занимают значительно меньше памяти, потому что:

- Нет словаря __dict__

- Атрибуты хранятся в структуре фиксированного размера (аналог массива)


Пример сравнения (при большом количестве объектов):


import sys


class PointDict:

def __init__(self, x, y):

self.x = x

self.y = y


class PointSlots:

__slots__ = ('x', 'y')

def __init__(self, x, y):

self.x = x

self.y = y


p1 = PointDict(1, 2)

p2 = PointSlots(1, 2)


print(sys.getsizeof(p1)) # ~56 байт (включая __dict__)

print(sys.getsizeof(p2)) # ~48 байт (только атрибуты)


Разница становится критичной при создании тысяч и миллионов объектов.


2. Защита от ошибок

Запрет на динамическое добавление атрибутов помогает ловить опечатки:


p = PointSlots(1, 2)

p.x_coor = 5 # AttributeError → сразу видна ошибка!


3. Ускорение доступа к атрибутам

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


Как правильно использовать __slots__?


Базовое использование: укажите кортеж или список строк с именами разрешённых атрибутов:


class Rectangle:

__slots__ = ('width', 'height')

def __init__(self, width, height):

self.width = width

self.height = height



Наследование и __slots__: если родительский класс использует __slots__, дочерний класс должен объявить свои собственные слоты:


class ColoredRectangle(Rectangle):

__slots__ = ('color',) # обязательно! Иначе появится __dict__

def __init__(self, width, height, color):

super().__init__(width, height)

self.color = color



Если дочерний класс не объявляет __slots__, он автоматически получит __dict__, и преимущества будут потеряны.


Пустой __slots__: можно указать пустой кортеж, если объект не должен иметь никаких атрибутов:


class Empty:

__slots__ = ()


Ограничения __slots__

Ограничение

Последствие

Нет __dict__

Невозможно добавлять атрибуты динамически

Нет __weakref__

Объекты нельзя использовать в weakref, если не добавить '__weakref__' в __slots__

Проблемы с множественным наследованием

Если два родителя имеют __slots__, может возникнуть конфликт

Не работает с __getattr__/__setattr__ без осторожности

Требуется дополнительная обработка


Пример для weakref:


import weakref


class SafeClass:

__slots__ = ('value', '__weakref__') # ← добавили __weakref__

def __init__(self, value):

self.value = value


obj = SafeClass(42)

ref = weakref.ref(obj) # Теперь работает


Когда использовать __slots__?


__slots__ используется, когда:

- Создаётся много однотипных объектов (точки, векторы, записи в базе данных);

- Нужна оптимизация памяти;

- Требуется строгий контроль над атрибутами;

- Класс представляет простую структуру данных/


Не использовать, когда:

- Нужна гибкость (динамические атрибуты);

- Класс предназначен для расширения пользователями;

- Работаете с множественным наследованием от нескольких классов со __slots__, если не уверены в совместимости;

- Планируете использовать сериализацию через pickle без дополнительной настройки.


Практический пример: оптимизация большого количества объектов


Представим, что вы разрабатываете игру с тысячами частиц:


# Без __slots__

class Particle:

def __init__(self, x, y, vx, vy):

self.x = x

self.y = y

self.vx = vx

self.vy = vy


# Со __slots__

class OptimizedParticle:

__slots__ = ('x', 'y', 'vx', 'vy')

def __init__(self, x, y, vx, vy):

self.x = x

self.y = y

self.vx = vx

self.vy = vy



При создании 1 000 000 объектов разница в потреблении памяти может составлять сотни мегабайт. В ресурсоёмких приложениях это критично.


Важные нюансы


  1. __slots__ не наследуется автоматически


Если родительский класс имеет __slots__, дочерний класс не унаследует их список — он должен объявить свои слоты явно. Однако атрибуты из родительских слотов остаются доступными.


class A:

__slots__ = ('x',)


class B(A):

__slots__ = ('y',) # не забудьте!


b = B()

b.x = 1 # OK — унаследован от A

b.y = 2 # OK — свой слот

b.z = 3 # Ошибка!


  1. __slots__ должен быть статическим атрибутом


Он задаётся на уровне класса, а не в __init__:


# Правильно

class Point:

__slots__ = ('x', 'y')


# Неправильно (не сработает)

class BadPoint:

def __init__(self):

self.__slots__ = ('x', 'y') # ← это просто обычный атрибут!



  1. Совместимость с dataclasses и namedtuple


В современном Python часто вместо ручного __slots__ используют:

- @dataclass(slots=True) — начиная с Python 3.10

- collections.namedtuple — для неизменяемых структур


Пример с dataclass:


from dataclasses import dataclass


@dataclass(slots=True)

class Point:

x: float

y: float


Это делает код ещё короче и чище.


Заключение


По умолчанию атрибуты объектов хранятся в __dict__ — это гибко, но неэффективно по памяти.

- __slots__ заменяет __dict__ на фиксированный набор атрибутов.

- Преимущества: экономия памяти, защита от ошибок, небольшой прирост скорости.

- Недостатки: потеря гибкости, сложности при наследовании.

- Используйте __slots__, когда создаёте много однотипных объектов и знаете все их атрибуты заранее.

- Не используйте __slots__ в общих, расширяемых или пользовательских классах.


Освоив __slots__, вы получите инструмент для написания эффективного, надёжного и профессионального кода — особенно в задачах, где важны производительность и контроль над данными.


Проверь себя


1. Что хранит __dict__ и почему он «дорогой»?

2. Как объявить __slots__ для класса с атрибутами name и age?

3. Можно ли добавить новый атрибут к объекту со __slots__? Почему?

4. Что произойдёт, если дочерний класс не объявит __slots__, а родитель — объявит?

5. Как добавить поддержку weakref к классу со __slots__?

6. В каких случаях __slots__ даёт наибольший выигрыш?

7. Чем @dataclass(slots=True) отличается от ручного __slots__?