Ответ
__slots__ в Python — это специальный атрибут класса, который позволяет явно объявить набор допустимых атрибутов для экземпляров этого класса. Его основное назначение — оптимизация использования памяти и, в некоторых случаях, ускорение доступа к атрибутам.
Как это работает:
По умолчанию каждый экземпляр класса в Python имеет словарь __dict__, который хранит все его атрибуты. Этот словарь занимает дополнительную память. Когда вы определяете __slots__, Python вместо __dict__ резервирует фиксированное пространство для каждого атрибута, указанного в __slots__, что значительно сокращает потребление памяти, особенно для классов с большим количеством экземпляров. Также __slots__ предотвращает создание __weakref__ для экземпляров, если он не указан явно в __slots__.
Преимущества:
- Экономия памяти: Значительное сокращение потребления памяти, так как экземпляры не хранят
__dict__. Это особенно критично для приложений, создающих миллионы объектов. - Ускорение доступа к атрибутам: Доступ к атрибутам через
__slots__может быть быстрее, чем через__dict__, поскольку Python не нужно выполнять поиск в словаре.
Ограничения и особенности:
- Невозможность динамического добавления атрибутов: После определения
__slots__вы не сможете добавлять новые атрибуты к экземплярам, кроме тех, что перечислены. Попытка сделать это вызоветAttributeError. - Наследование:
- Если дочерний класс не определяет
__slots__, он будет иметь__dict__. - Если дочерний класс определяет
__slots__, он должен включать атрибуты родительского__slots__(или Python будет использовать__dict__для родительских атрибутов). - Если вы хотите, чтобы экземпляры могли иметь
__dict__или__weakref__в дополнение к__slots__, их нужно явно включить в список__slots__.
- Если дочерний класс не определяет
- Множественное наследование: Может быть сложным, если несколько базовых классов имеют
__slots__.
Пример:
class PersonWithSlots:
__slots__ = ['name', 'age'] # Разрешенные атрибуты
def __init__(self, name, age):
self.name = name
self.age = age
class PersonWithoutSlots:
def __init__(self, name, age):
self.name = name
self.age = age
p_slots = PersonWithSlots("Alice", 30)
print(f"PersonWithSlots: {p_slots.name}, {p_slots.age}")
# print(p_slots.__dict__) # AttributeError: 'PersonWithSlots' object has no attribute '__dict__'
try:
p_slots.job = "Engineer"
except AttributeError as e:
print(f"Ошибка при добавлении нового атрибута: {e}")
p_no_slots = PersonWithoutSlots("Bob", 25)
print(f"PersonWithoutSlots: {p_no_slots.name}, {p_no_slots.age}")
print(f"__dict__ PersonWithoutSlots: {p_no_slots.__dict__}") # {'name': 'Bob', 'age': 25}
p_no_slots.job = "Developer" # OK
print(f"PersonWithoutSlots с новым атрибутом: {p_no_slots.job}")
Когда использовать __slots__:
Используйте __slots__ только тогда, когда вы работаете с большим количеством экземпляров класса (тысячи или миллионы) и точно знаете, какие атрибуты будут у ваших объектов. В большинстве обычных случаев его использование не требуется и может усложнить код.