> For the complete documentation index, see [llms.txt](https://igor-19.gitbook.io/qa/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://igor-19.gitbook.io/qa/avtomatizaciya-testirovaniya/python/oop-v-python-vo-vsekh-podrobnostyakh/__slots__-v-klasse-i-pri-nasledovanii.md).

# \_\_slots\_\_ в классе и при наследовании

* [https://ru.stackoverflow.com/questions/1206730/Какова-цель-slots-в-python](https://ru.stackoverflow.com/questions/1206730/%D0%9A%D0%B0%D0%BA%D0%BE%D0%B2%D0%B0-%D1%86%D0%B5%D0%BB%D1%8C-slots-%D0%B2-python)
* <https://proproprogs.ru/python_oop/python-kak-rabotaet-slots-s-property-i-pri-nasledovanii>

```python
class AbstractA(ABC):
    __slots__ = ()

class AbstractB(ABC):
    __slots__ = ()


class BaseA(AbstractA): 
    __slots__ = ('a',)

class BaseB(AbstractB): 
    __slots__ = ('b',)

class Child(AbstractA, AbstractB): 
    __slots__ = ('a', 'b')

c = Child()
1234567891011121314151617
```

То есть мы вместо того, чтобы наследоваться от классов с конкретной реализацией (*BaseA*, *BaseB*), **наследуемся от абстрактных классов**

Однако `__slots__` может вызвать проблемы при множественном наследовании.

```python
class BaseA(object): 
    __slots__ = ('a',)

class BaseB(object): 
    __slots__ = ('b',) 
12345
```

Создание дочернего класса от родителей с обоими непустыми слотами не удается:

```python
>>> class Child(BaseA, BaseB): __slots__ = ()
Traceback (most recent call last):
  File "<pyshell#68>", line 1, in <module>
    class Child(BaseA, BaseB): __slots__ = ()
TypeError: Error when calling the metaclass bases
    multiple bases have instance lay-out conflict 
123456
```

Если вы столкнетесь с этой проблемой, вы можете просто удалить `__slots__` у родителей или, если вы контролируете родителей, дать им пустые слоты или выполнить рефакторинг для абстракций:

```python
from abc import ABC

class AbstractA(ABC):
    __slots__ = ()

class BaseA(AbstractA): 
    __slots__ = ('a',)

class AbstractB(ABC):
    __slots__ = ()

class BaseB(AbstractB): 
    __slots__ = ('b',)

class Child(AbstractA, AbstractB): 
    __slots__ = ('a', 'b')

c = Child() # Нет ошибок
123456789101112131415161718
```

Добавьте `'__dict__'` к `__slots__` чтобы получить динамическое назначение:

```python
class Foo(object):
    __slots__ = 'bar', 'baz', '__dict__'
12
```

и сейчас

```python
>>> foo = Foo()
>>> foo.boink = 'boink'
12
```

Таким образом, с `'__dict__'` в слотах мы теряем некоторые преимущества размера с преимуществом наличия динамического назначения и по-прежнему наличия слотов для имен, которые мы ожидаем

Когда вы наследуете объект, который не имеет слотов, вы получаете такую же семантику, когда используете `__slots__` - имена, которые находятся в `__slots__`, указывают на значения, размещенные в слотах, тогда как любые другие значения помещаются в `__dict__` экземпляра.

Избегать `__slots__`, потому что вы хотите иметь возможность добавлять атрибуты на лету, на самом деле не является хорошей причиной - просто добавьте `'__dict__'` в свой `__slots__`, если это необходимо.

Вы можете точно так же явно добавить `__weakref__` в `__slots__`, если вам нужна эта функция.

***

**Подводя итоги:** если вы составляете [миксины](https://stackoverflow.com/questions/860245/mixin-vs-inheritance/27907511#27907511) или используете [абстрактные базовые классы](https://stackoverflow.com/questions/372042/difference-between-abstract-class-and-interface-in-python/31439126#31439126), которые не предназначены для создания экземпляров, пустой `__slots__`  в этих родителях кажется лучшим способом гибкости для подклассов.

Чтобы продемонстрировать, сначала давайте создадим код с классом, который мы хотели бы использовать при множественном наследовании.

```python
class AbstractBase:
    __slots__ = ()
    def __init__(self, a, b):
        self.a = a
        self.b = b
    def __repr__(self):
        return f'{type(self).__name__}({repr(self.a)}, {repr(self.b)})'
1234567
```

Мы могли бы использовать вышесказанное непосредственно путем наследования и объявления ожидаемых слотов:

```python
class Foo(AbstractBase):
    __slots__ = 'a', 'b' 
12
```

Но нас это не волнует, это тривиальное одиночное наследование, нам нужен другой класс, от которого мы также могли бы унаследовать, возможно, с шумным атрибутом:

```python
class AbstractBaseC:
    __slots__ = ()
    @property
    def c(self):
        print('getting c!')
        return self._c
    @c.setter
    def c(self, arg):
        print('setting c!')
        self._c = arg
12345678910
```

Теперь, если бы на обеих базах были непустые слоты, мы не смогли бы сделать следующее. (На самом деле, если бы мы хотели, мы могли бы дать `AbstractBase` непустые слоты `a` и `b` и исключить их из приведенного ниже объявления - оставлять их было бы неправильно):

```python
class Concretion(AbstractBase, AbstractBaseC):
    __slots__ = 'a b _c'.split() 
12
```

И теперь у нас есть функциональность от обоих через множественное наследование, и мы все еще можем запретить создание экземпляров `__dict__` и `__weakref__`:

```python
>>> c = Concretion('a', 'b')
>>> c.c = c
setting c!
>>> c.c
getting c!
Concretion('a', 'b')
>>> c.d = 'd'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Concretion' object has no attribute 'd'
```
