Быстрый в изучении - мощный в программировании
>> Telegram ЧАТ для Python Программистов

Свободное общение и помощь советом и решением проблем с кодом! Заходите в наш TELEGRAM ЧАТ!

>> Python Форум Помощи!

Мы создали форум где отвечаем на все вопросы связанные с языком программирования Python. Ждем вас там!

>> Python Канал в Telegram

Обучающие статьи, видео и новости из мира Python. Подпишитесь на наш TELEGRAM КАНАЛ!

Слоты и обобщенные инструмены в Python

Слоты и обобщенные инструмены в Python

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

Обобщенные инструменты, которые получают списки атрибутов или обращаются к атрибутам, используя имена в виде строк, например, должны использовать более универсальные механизмы, чем атрибут __dict__. К таким механизмам можно отнести встроенные функции getattr, setattr и dir, способные отыскивать атрибуты в обоих хранилищах, __dict__ и __slots__. В некоторых случаях для полноты картины может потребоваться проверить оба источника атрибутов.

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

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


class MyClass:
...    __slots__ = ['a', 'b'] # По умолчанию наличие __slots__ означает отсутствие __dict__
>>> ZZZ = MyClass()
>>> ZZZ.a = 1
>>> ZZZ.a
1
>>> ZZZ.__dict__
AttributeError: 'ZZZ' object has no attribute '__dict__'
>>> getattr(ZZZ, 'a')
1
>>> setattr(ZZZ, 'b', 10)
>>> ZZZ.b
10
>>> 'a' in dir(ZZZ)
True
>>> 'b' in dir(ZZZ)
True

В отсутствие словаря с пространством имен невозможно присвоить значения атрибутам экземпляра, имена которых отсутствуют в списке слотов:


class MySecondClass:
...    __slots__ = ['a', 'b']
...    def __init__(self):
...        self.d = 4 # Невозможно добавить новый атрибут, когда отсутствует атрибут __dict__
>>> QQQ = MySecondClass()
AttributeError: 'QQQ' object has no attribute 'd'

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


>>> class MyThirdClass:
...     __slots__ = ['a', 'b', '__dict__'] # Добавить __dict__ в слоты
...     c = 250 # Атрибуты класса действуют как обычно
...     def __init__(self):
...         self.d = 55 # Имя d будет добавлено в __dict__, а не в __slots__
...
>>> KKK = MyThirdClass()
>>> KKK.d
55
>>> KKK.__dict__ # Некоторые объекты имеют оба атрибута, __dict__ и __slots__
{'d':55} # getattr() может извлекать атрибуты любого типа
>>> KKK.__slots__
['a', 'b', '__dict__']
>>> KKK.c
250
>>> KKK.a # Все атрибуты экземпляра не определены
AttributeError: a # Пока им не будет присвоено значение
>>> KKK.a = 123
>>> KKK.b = 100
>>> getattr(KKK, 'a'), getattr(KKK, 'c'), getattr(KKK, 'd')
(123, 250, 55)

Если потребуется реализовать универсальный способ получения значений всех атрибутов экземпляров, необходимо учесть наличие двух форм хранения атрибутов или использовать функцию dir, которая дополнительно возвращает все унаследованные атрибуты(для получения ключей в следующем примере используется итератор словаря):


>>> for attr in list(KKK.__dict__) + KKK.__slots__:
...     print(attr, ' => ', getattr(KKK, attr))
d => 55
a => 123
b => 100
__dict__ => {'d' : 55}

Поскольку любой из этих атрибутов может отсутствовать, более правильный способ выглядит, как показано ниже(функция getattr позволяет определять возвращаемое значение по умолчанию):


>>> for attr in list(getattr(KKK, '__dict__', [])) + getattr(KKK, '__slots__', []):
...     print(attr, ' => ', getattr(KKK, attr))
d => 55
a => 123
b => 100
__dict__ => {'d' : 55}