Специальный атрибут __slots__ позволяет вам явно указать, какими атрибутами должны обладать экземпляры ваших объектов, с ожидаемыми результатами:
- более быстрый доступ к атрибутам.
- экономия места в памяти.
Экономия места происходит за счет
- Хранения ссылок на значения в слотах вместо __dict__.
- Отказ от создания __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__ )
Преимущества использования слотов:
- Слоты быстрее, чем __dict__.
- Слоты экономнее расходуют память.
- Слоты более безопасны.
- Слоты более удобны.
- Слоты более удобны для чтения.
- Слоты более эффективны.
Недостатки использования слотов:
- Слоты не совместимы с множественным наследованием и миксинами.
- Слоты не совместимы с метаклассами.
- Слоты не совместимы с ABC.
- Слоты не совместимы с pickling.
- Слоты не совместимы с копированием.
- Слоты не совместимы с 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()
Есть много вещей, которые мы можем сделать со слотами, чтобы помочь улучшить скорость нашего кода. Попробуйте как можно больше и поделитесь с другими.
Счастливого кодирования!!!!