Ускорение работы классов Python с помощью слотов

Специальный атрибут __slots__ позволяет вам явно указать, какими атрибутами должны обладать экземпляры ваших объектов, с ожидаемыми результатами:

  1. более быстрый доступ к атрибутам.
  2. экономия места в памяти.

Экономия места происходит за счет

  1. Хранения ссылок на значения в слотах вместо __dict__.
  2. Отказ от создания __dict__ и __weakref__, если родительские классы запрещают их, а вы объявляете __slots__.

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

Давайте создадим простой класс Person, который изначально имеет 2 атрибута first_name и last_name. Если мы выведем __dict__ объекта, то получим ключ и значение каждого атрибута. Тем временем мы также выводим __dict__ класса, который понадобится позже. После этого к объекту добавляется новый атрибут reviewer, и мы можем увидеть его в обновленном __dict__.

class Person:
     def __init__ (self, first_name: str, last_name: str):
         self.first_name = first_name
         self.last_name = last_name

if __name__ == " __main__":
     person = Person("Chidozie", "Okafor")
     print(person. __dict__ )
  {'first_name': 'Chidozie', 'last_name': 'Okafor'}
    print(Person. __dict__ )
{' __module__': ' __main__', ' __doc__': None, ' __init__': <function __init__ at 0x10e9cd140>}
Вход в полноэкранный режим Выход из полноэкранного режима

Проблемы со словарем заключаются в потреблении памяти, а также в том, что доступ к словарю включает в себя хэширование, словарь фактически является хэш-картой. Наихудший случай временной сложности get/set в хэш-карте — O(n).

Слоты предоставляют специальный механизм для уменьшения размера объектов. Это концепция оптимизации памяти на объектах.

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

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

Чтобы создать слот, достаточно добавить поле __slots__ или slots=True, если мы используем класс данных.

class PersonSlot:
     __slots__ = ["first_name", "last_name"]
     def __init__ (self, first_name: str, last_name: str):
         self.first_name = first_name
         self.last_name = last_name

if __name__ == " __main__":
     person = Person("Chidozie", "Okafor")
     print(person. __dict__ )
  {'first_name': 'Chidozie', 'last_name': 'Okafor'}
    print(Person. __dict__ )
{' __module__': ' __main__', ' __slots__': ['first_name', 'last_name'], ' __init__': <function __init__ at 0x10432b140>, ' __doc__': None}
Вход в полноэкранный режим Выход из полноэкранного режима

Чтобы избежать повторения имен переменных, мы можем использовать dataclasses для создания класса, который будет автоматически создавать слоты для нас.

from dataclasses import dataclass

@dataclass(slots=True)
class PersonSlot:
      first_name: str
      last_name: str

if __name__ == " __main__":
   person = PersonSlot("Chidozie", "Okafor")
   print(person. __dict__ )
   print(PersonSlot. __dict__ )
Вход в полноэкранный режим Выход из полноэкранного режима

Преимущества использования слотов:

  1. Слоты быстрее, чем __dict__.
  2. Слоты экономнее расходуют память.
  3. Слоты более безопасны.
  4. Слоты более удобны.
  5. Слоты более удобны для чтения.
  6. Слоты более эффективны.

Недостатки использования слотов:

  1. Слоты не совместимы с множественным наследованием и миксинами.
  2. Слоты не совместимы с метаклассами.
  3. Слоты не совместимы с ABC.
  4. Слоты не совместимы с pickling.
  5. Слоты не совместимы с копированием.
  6. Слоты не совместимы с deepcopy.

Давайте посмотрим, насколько быстро работают слоты по сравнению с обычным словарем.

import timeit
from dataclasses import dataclass
from functools import partial

@dataclass(slots=False)
class Person:
    first_name : str
    last_name: str

@dataclass(slots=True)
class PersonSlot:
    first_name : str
    last_name : str

def get_set_delete(person: Person | PersonSlot):
     person.first_name = "Raphael"
     _ = person.first_name
     del person.first_name

def get_percentage_of_performance():
    person = Person("Chidozie", "Okafor")
    person_slot = PersonSlot("Chidozie", "Okafor")
    no_slots = min(timeit.repeat(partial(get_set_delete, person), number=100, repeat=3))
    slots = min(timeit.repeat(partial(get_set_delete, person_slot), number=100, repeat=3))
    print(f"No Slots: {no_slots}") No Slots: 3.3914000596269034e-05

print(f"Slots: {slots}") Slots: 1.8151000404031947e-05
    print(f"Percentage of performance: {(slots / no_slots) * 100}") Percentage of performance: 53.520670180175635

if __name__ == " __main__":
    get_percentage_of_performance()
Войти в полноэкранный режим Выход из полноэкранного режима

Есть много вещей, которые мы можем сделать со слотами, чтобы помочь улучшить скорость нашего кода. Попробуйте как можно больше и поделитесь с другими.

Счастливого кодирования!!!!

Оцените статью
Procodings.ru
Добавить комментарий