__slots__ Python

Содержание
Введение
Пример
Разница в поведении
Проверка экономии памяти
Проверка скорости
__slots__ при наследовании
Далее по теме

Введение

Официальная документация: docs , wiki

Статья на сайте SQLAlchemy - Significant Improvements in Structural Memory Use

Пример

Без использования __slots__ у каждого экземпляра класса есть свой собственный словарь атрибутов

class Developer: def __init__(self, name, age, salary, framework): self.name = name self.age = age self.salary = salary self.framework = framework employee1 = Developer("Andrei", 36, 5000, "FastAPI") print(employee1.__dict__) print(hasattr(employee1, "__slots__"))

{'name': 'Andrei', 'age': 36, 'salary': 5000, 'framework': 'FastAPI'} False

При использовании __slots__ отдельные словари не создаются

class Developer: __slots__ = ("name", "age", "salary", "framework") def __init__(self, name, age, salary, framework): self.name = name self.age = age self.salary = salary self.framework = framework employee1 = Developer("Andrei", 36, 5000, "FastAPI") print(employee1.__dict__)

Traceback (most recent call last): File "C:\Users\Andrei\slots_demo.py", line 13, in <module> print(employee1.__dict__) AttributeError: 'Developer' object has no attribute '__dict__'

Можно обратиться к атрибуту __slots__

print(employee1.__slots__) print(hasattr(employee1, "__slots__"))

('name', 'age', 'salary', 'framework') True

Разница в поведении

Попробуем добавить аттрибут к обычному классу

class Regular: def __init__(self, name, age): self.name = name self.age = age regular = Regular("Eugene", 59) regular.city = "Benalmadena" print(regular.city)

Benalmadena

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

class Slotted: __slots__ = ("name", "age") def __init__(self, name, age): self.name = name self.age = age slotted = Slotted("Andrei", 36) slotted.city = "Malaga" print(slotted.city)

Traceback (most recent call last): File "C:\Users\Andrei\slots_demo.py", line 10, in <module> slotted.city = "Malaga" ^^^^^^^^^^^^ AttributeError: 'Slotted' object has no attribute 'city'

Проверка экономии памяти

Если теперь в оба примера (без __slots__ и со __slots__) добавить проверку размера объекта метдом asizeof из pympler, окажется, что объект, где применялся __slots__ больше

from pympler import asizeofprint(f"asizeof() slotted size: {asizeof.asizeof(slotted_emp)}")print(f"asizeof() not slotted size: {asizeof.asizeof(not_slotted_emp)}")

asizeof() slotted size: 240 asizeof() not slotted size: 560

Экономия приличная, но если воспользоваться другим измерителем размера, результат сильно исказится, подробнее в статье «Размер объекта в Python»

Проверка скорости

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

import timeit from sys import getsizeof class Developer: __slots__ = ("name", "age", "salary", "framework") def __init__(self, name, age, salary, framework): self.name = name self.age = age self.salary = salary self.framework = framework class Programmer: def __init__(self, name, age, salary, framework): self.name = name self.age = age self.salary = salary self.framework = framework slotted_emp = Developer("Andrei", 36, 5000, "FastAPI") not_slotted_emp = Programmer("Eugene", 59, 95000, "Spring") # print(employee1.__dict__) # print(employee1.__slots__) def get_set_delete_fn(obj): def get_set_del(): obj.name = "foo" obj.name del obj.name obj.age = "foo" obj.age del obj.age obj.salary = "foo" obj.salary del obj.salary return get_set_del print(f"min slotted: {min(timeit.repeat(get_set_delete_fn(slotted_emp)))}") print(f"max slotted: {max(timeit.repeat(get_set_delete_fn(slotted_emp)))}") print(f"min not slotted: {min(timeit.repeat(get_set_delete_fn(not_slotted_emp)))}") print(f"max not slotted: {max(timeit.repeat(get_set_delete_fn(not_slotted_emp)))}")

min slotted: 0.10821229999999998 max slotted: 0.11044899999999991 min not slotted: 0.14354210000000012 max not slotted: 0.14311980000000002

Ускорение от __slotted__ в нашем примере очевидно.

__slots__ при наследовании

В родительском классе в __slots__ указываются его атрибуты. В производном классе в __slots__ нужно перечислять только новые для этого класса атрибуты. Перечислять заново родительские не нужно.

class Employee: __slots__ = ("name", "age", "salary") def __init__(self, name, age, salary): self.name = name self.age = age self.salary = salary def increase_salary(self, percent): self.salary += self.salary * (percent/100) class Developer(Employee): __slots__ = ("prog_language", "framework",) def __init__(self, name, age, salary, prog_language, framework): super().__init__(name, age, salary) self.prog_language = prog_language self.framework = framework class Tester(Employee): __slots__ = ("test_framework",) def __init__(self, name, age, salary, test_framework): super().__init__(name, age, salary) self.test_framework = test_framework def run_tests(self): print(f"Testing is started by {self.name}...") print("Tests are done.") employee1 = Developer("Andrei", 36, 5000, "python", "FastAPI") employee2 = Tester("Cem", 36, 5000, "PyTest") print(employee1.salary) employee1.increase_salary(10) print(employee1.salary) employee2.run_tests()

5000 5500.0 Testing is started by Cem... Tests are done.

Недостатки

При использовании __slots__ пропадает возможность динамически добавлять атрибуты. Можно пользоваться только тем, что определено в __slots__

При использовании множественного наследования __slots__ нужно указывать во всех родителях, хотя бы пустым.

Похожие статьи
ООП в Python
Классы
Методы
class variables
class methods
Статические методы
Наследование
Специальные методы
dataclass
__slots__
Декоратор property
super()

Поиск по сайту

Подпишитесь на Telegram канал @aofeed чтобы следить за выходом новых статей и обновлением старых

Перейти на канал

@aofeed

Задать вопрос в Телеграм-группе

@aofeedchat

Контакты и сотрудничество:
Рекомендую наш хостинг beget.ru
Пишите на info@urn.su если Вы:
1. Хотите написать статью для нашего сайта или перевести статью на свой родной язык.
2. Хотите разместить на сайте рекламу, подходящую по тематике.
3. Реклама на моём сайте имеет максимальный уровень цензуры. Если Вы увидели рекламный блок недопустимый для просмотра детьми школьного возраста, вызывающий шок или вводящий в заблуждение - пожалуйста свяжитесь с нами по электронной почте
4. Нашли на сайте ошибку, неточности, баг и т.д. ... .......
5. Статьи можно расшарить в соцсетях, нажав на иконку сети: