На этом занятии мы
с вами познакомимся со специальной коллекцией __slots__, которую
можно прописывать в любом классе языка Python. Для простоты
восприятия информации возьмем простой класс для представления точки на
плоскости:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
Пока в этом
классе нет коллекции __slots__, поэтому при создании его экземпляра
можно свободно оперировать существующими свойствами:
pt = Point(1, 2)
pt.x
pt.y = 100
И создавать
новые:
Как известно,
список всех локальных свойств экземпляра класса хранится в его магическом
списке __dict__:
Но что, если мы
хотим объявить класс точки на плоскости, чтобы у его экземпляров были только
два свойства x и y и никакие
другие. Как это сделать? Для этого, как раз и применяется коллекция __slots__.
Давайте я создам
для сравнения еще один класс, в котором укажу эту коллекцию:
class Point2D:
__slots__ = ('x', 'y')
def __init__(self, x, y):
self.x = x
self.y = y
Смотрите, мы в
кортеже перечисляем атрибуты с именами x и y (указываются в
виде строки) и только такие локальные свойства могут существовать в экземплярах
этого класса. Проверим это. Создадим экземпляр:
Выведем свойства
x и y, убедимся, что
они присутствуют в экземпляре:
Но вот добавить
новое уже не получится:
Это произошло
как раз по тому, что в классе прописана коллекция __slots__ и мы ее можем
даже вывести:
А вот привычная
нам коллекция
будет
отсутствовать. То есть, класс и его экземпляры ведут себя немного по-другому. Мы
совершенно спокойно можем изменять, удалять и добавлять локальные свойства x, y:
pt2.x = 50
del pt2.y
pt2.y = 100
Но только их и
никакие другие. Причем, обратите внимание, речь идет только о локальных
свойствах экземпляров. В сам класс мы по-прежнему можем добавлять любые
атрибуты, например, MAX_COORD:
class Point2D:
__slots__ = ('x', 'y')
MAX_COORD = 100
def __init__(self, x, y):
self.x = x
self.y = y
Тогда в
экземплярах этого класса появится это дополнительное свойство со значением 100.
Коллекция __slots__ помимо
ограничения создаваемых локальных свойств, еще уменьшает объем памяти,
занимаемый экземпляром класса. Смотрите, если создать два экземпляра:
pt = Point(1, 2)
pt2 = Point2D(10, 20)
То первый будет
содержать ссылку, как на объект класса, так и на коллекцию __dict__. А второй –
только на пространство имен класса Point2D:
print(pt.__sizeof__(), pt.__dict__.__sizeof__())
print(pt2.__sizeof__())
И еще коллекция
__slots__ ускоряет
работу с локальными свойствами экземпляров класса. Например, если добавить в
каждый класс метод:
def calc(self):
self.x += 1
del self.y
self.y = 0
И замерить скорость
его работы:
t1 = timeit.timeit(pt.calc)
t2 = timeit.timeit(pt2.calc)
print(t1, t2)
То мы увидим,
что t1 больше, чем t2, то есть,
класс Point2D работает
быстрее с локальными свойствами, чем класс Point.
Вот
такие три особенности дает коллекция __slots__ экземплярам
класса:
- ограничение
создаваемых локальных свойств;
- уменьшение
занимаемой памяти;
- ускорение работы
с локальными свойствами.
На следующем
занятии мы продолжим эту тему и посмотрим, как эта коллекция ведет себя при
наследовании классов.