# \_\_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'
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://igor-19.gitbook.io/qa/avtomatizaciya-testirovaniya/python/oop-v-python-vo-vsekh-podrobnostyakh/__slots__-v-klasse-i-pri-nasledovanii.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
