Перейти к содержанию

Cлоты

__slots__ позволяет задать ограниченный набор атрибутов, которыми будет обладать экземпляр класса.

Python, как и другие динамические языки (например JavaScript), славен тем, что с объектами и экземплярами в рантайме можно творить практически что угодно — добавлять атрибуты, удалять и изменять их. Это очень удобно и гибко, но всему есть своя цена. И цена здесь - это понижение скорости доступа к аттрибутам и дополнительный расход памяти.

При этом важно понимать, что такое поведение нам нужно не всегда. Бывают случаи, когда мы точно знаем, какие аттрибуты будут у наших экзепляров классов. Или же мы хотим ограничить добавление новых аттрибутов. Именно для этого и существует __slots__

Задание слотов

Слоты задаются через аттрибут __slots__ в классе:

class SlotsClass:
    __slots__ = ('foo', 'bar')

>>> obj = SlotsClass()
>>> obj.foo = 5
>>> obj.foo
# 5
>>> obj.another_attribute = 'test'
Traceback (most recent call last):
  File "python", line 5, in <module>
AttributeError: 'SlotsClass' object has no attribute 'another_attribute'

Теперь мы не можем добавлять аттрибуты в наши объекты. Тем более скорость доступа к аттрибутам повышается на 25-30%, потому что их вычислять теперь не надо.

Экономия памяти

При задании __slots__ __dict__ у класса не создается, за счет этого экономится память.

Слабые ссылки

По-умолчанию слабые ссылки на экземпляры классов со слотами не работают, по причине того, что ещё перестает создавать аттрибут __weakref__. Решается добавлением __weakref__ в __slots__.

Наследование

  1. При наследовании класса с __slots__ новый класс конечно унаследует __slots__, но это не помешает созданию __dict__, со всеми вытекающими расходами и фичами:

    class SlotsClass:
        __slots__ = ('foo', 'bar')
    
    class ChildSlotsClass(SlotsClass):
        pass
    
    >>> obj = ChildSlotsClass()
    >>> obj.__slots__
    # ('foo', 'bar')
    >>> obj.foo = 5
    >>> obj.test = 3
    >>> obj.__dict__
    # {'test': 3}
    

  2. Если мы хотим чтобы __slots__ наследовались нормально и __dict__ не создавался, необходимо чтобы класс ChildSlotsClass тоже реализовал __slots__:

    class SlotsClass:
        __slots__ = ('foo', 'bar')
    
    class ChildSlotsClass(SlotsClass):
        __slots__ = ('baz',)
    
    >>> obj = ChildSlotsClass()
    >>> obj.foo = 5
    >>> obj.baz = 6
    >>> obj.something_new = 3
    Traceback (most recent call last):
      File "python", line 12, in <module>
    AttributeError: 'ChildSlotsClass' object has no attribute 'something_new'
    

  3. За множественное наследование классов у которых есть слоты можно забыть, получим TypeError.

Почитать