Рассмотрим два важных аспекта использования
__slots__ — взаимодействие с декоратором property и особенности наследования.🔧 slots и property
• Возьмём за основу класс Point из прошлого поста.
• Добавим в slots еще одно допустимое свойство - 'length'.
• А что будет, если добавить property с таким же именем 'length'?
class Point2D:
__slots__ = ('x', 'y', '__length') # Используем защищенный атрибут
def __init__(self, x, y):
self.x = x
self.y = y
self.__length = (x**2 + y**2)**0.5
@property
def length(self):
return self.__length
@length.setter
def length(self, value):
self.__length = value
✅ Работает отлично! Потому что:
• length в property — это атрибут класса, а не локальное свойство экземпляра.
• slots не запрещает работу с такими атрибутами!
🔧 slots и наследование
Базовый класс:
class Point:
__slots__ = ('x', 'y')
def __init__(self, x, y):
self.x = x
self.y = y
❌ Дочерний класс БЕЗ __slots__:
class Point3D(Point):
pass
pt3 = Point3D(10, 20)
pt3.z = 30 # ✅ Работает!
print(pt3.__dict__) # {'z': 30} - x и y не в dict!
📌 Важно: если не определить slots в дочернем классе, у объектов появится dict и можно создавать любые атрибуты!
✅ Дочерний класс С __slots__:
class Point3D(Point):
__slots__ = ('z',) # Добавляем только новые атрибуты
pt3 = Point3D(10, 20)
pt3.z = 30 # ✅ Работает
pt3.w = 40 # ❌ Ошибка! Только x, y, z разрешены
🎯 Ключевые моменты:
1)
__slots__ работает с property — используйте защищенные имена (__length) в слотах2) Наследование требует явного указания
__slots__ в дочерних классах3) Слоты объединяются — дочерний класс получает все слоты из цепочки наследования
4) Без
__slots__ в потомке — объекты получают __dict__ и теряют преимущества оптимизации📝 Правило для наследования:
Если базовый класс имеет
__slots__, определите __slots__ и в дочернем классе, даже если он пустой!class Child(Parent):
__slots__ = () # Пустой кортеж сохраняет оптимизацию
Используйте slots осознанно, учитывая эти нюансы! 🚀