Для чего же нужны __slots__?
Снизить потребление памяти, экземпляром класса, путем ограничения количества поддерживаемых атрибутов.
При использовании __slots__, атрибут __dict__ будет недоступен. Если нужно иметь __dict__, его нужно добавить в __slots__, это будет означать что динамические атрибуты будут работать, тоесть добавляться в словарь __dict__.
Пример:
class A(object):
"""Класс со слотами.
"""
__slots__ = ('a', 'b', 'c')
Соответственно при попытке доступа в __dict__ получим исключение:
In [1]: a = A()
In [2]: a.__dict__
---------------------------------------------------------
AttributeError Traceback (most recent call last)
in ()
----> 1 a.__dict__
AttributeError: 'A' object has no attribute '__dict__'
In [3]: dir(a)
Out[3]:
['__class__',
'__delattr__',
'__doc__',
'__format__',
'__getattribute__',
'__hash__',
'__init__',
'__module__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__slots__',
'__str__',
'__subclasshook__',
'a',
'b',
'c']
Если добавить __dict__ в слоты
class A(object):
"""Класс со слотами.
"""
__slots__ = ('a', 'b', 'c', '__dict__')
Тогда:
In [4]: a = A()
In [5]: a.__dict__
Out[5]: {}
In [6]: dir(a)
Out[6]:
['__class__',
'__delattr__',
'__dict__',
'__doc__',
'__format__',
'__getattribute__',
'__hash__',
'__init__',
'__module__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__slots__',
'__str__',
'__subclasshook__',
'a',
'b',
'c']
Не имея атрибута __weakref__, экземпляры классов со __slots__ не поддерживают слабые ссылки на себя. Для поддержки слабых ссылок нужно добавить в перечень атрибутов __weakref__ по тому же принципу как и __dict__.При наследовании от класса со слотами если не объявлены слоты, будет доступен __dict__ соответственно и динамические аттрибуты будут работать. При этом поведение атрибутов объявленых в слотах родительского класса не пеняется.
class A(object):
"""Класс со слотами.
"""
__slots__ = ('a', 'b', 'c')
class B(A):
"""Класс без слотов, но наследующий класс со слотами.
"""
pass
In [7]: b = B()
In [8]: dir(b)
Out[8]:
['__class__',
'__delattr__',
'__dict__',
'__doc__',
'__format__',
'__getattribute__',
'__hash__',
'__init__',
'__module__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__slots__',
'__str__',
'__subclasshook__',
'__weakref__',
'a',
'b',
'c']
In [9]: b.__dict__
Out[9]: {}
In [10]: b.a
---------------------------------------------------------
AttributeError Traceback (most recent call last)
in ()
----> 1 b.a
AttributeError: a
При наследовании от класса не имеющего __slots__, родителем будет создан __dict__
class C(object):
"""Класс без слотов.
"""
pass
class D(C):
"""Класс со слотами, но наследующий класс без слотов.
"""
__slots__ = ('q', 'w', 'e')
In [11]: c = C()
In [12]: d = D()
In [13]: c.__dict__
Out[13]: {}
In [14]: d.__dict__
Out[14]: {}
In [15]: d.__slots__
Out[15]: ('q', 'w', 'e')
In [16]: dir(c)
Out[16]:
['__class__',
'__delattr__',
'__dict__',
'__doc__',
'__format__',
'__getattribute__',
'__hash__',
'__init__',
'__module__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__',
'__weakref__']
In [17]: dir(d)
Out[17]:
['__class__',
'__delattr__',
'__dict__',
'__doc__',
'__format__',
'__getattribute__',
'__hash__',
'__init__',
'__module__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__slots__',
'__str__',
'__subclasshook__',
'__weakref__',
'e',
'q',
'w']
И еще, нельзя определить __slots__ для классов наследующихся от встроенных типов переменной длины, например long, str и tuple. Точнее определить можно, но ненужно :) так как получите исключение TypeError
Давай с замерами. Сколько это реально сэкономит памяти? И в каких ситуациях есть смысл заморачиваться?
ОтветитьУдалитьХм...
ОтветитьУдалитьДумаю заморочусь на эту тему и замучу сравнение.