Ответ 1
__slots__
не ускоряет доступ к атрибутам (значительно):
>>> class Foo(object):
... __slots__ = ('spam',)
... def __init__(self):
... self.spam = 'eggs'
...
>>> class Bar(object):
... def __init__(self):
... self.spam = 'eggs'
...
>>> import timeit
>>> timeit.timeit('t.spam', 'from __main__ import Foo; t=Foo()')
0.07030296325683594
>>> timeit.timeit('t.spam', 'from __main__ import Bar; t=Bar()')
0.07646608352661133
Целью использования __slots__
является сохранение памяти; вместо использования отображения .__dict__
в экземпляре класс объекты дескрипторов для каждого атрибута, названного в __slots__
, и экземпляры имеют атрибут, присвоенный им или нет, имеет фактическое значение:
>>> class Foo(object):
... __slots__ = ('spam',)
...
>>> dir(Foo())
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'spam']
>>> Foo().spam
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: spam
>>> Foo.spam
<member 'spam' of 'Foo' objects>
>>> type(Foo.spam)
<type 'member_descriptor'>
Таким образом, python все равно должен смотреть на класс для каждого доступа к атрибуту в экземпляре Foo
(чтобы найти дескриптор). Любой неизвестный атрибут (скажем, Foo.ham
) по-прежнему будет приводить к тому, что Python просматривает класс MRO для поиска этого атрибута и включает поиск словаря. И вы все равно можете назначить дополнительные атрибуты классу:
>>> Foo.ham = 'eggs'
>>> dir(Foo)
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'ham', 'spam']
>>> Foo().ham
'eggs'
Дескрипторы слота создаются при создании класса и получают доступ к памяти, назначенной каждому экземпляру, для хранения и получения ссылки на соответствующее значение (тот же кусок памяти, который отслеживает количество ссылок экземпляров и ссылку на объект класса). Без слотов дескриптор для __dict__
используется для доступа к ссылке на объект dict
таким же образом.