What is the goal __ Slots __ in Python. Especially in relation to when to use it, and when not?
Translation of the question from the participant
question@mail.ru
·
01.01.1970 03:00
What is the goal __ Slots __ in Python. Especially in relation to when to use it, and when not?
Translation of the question from the participant
answer@mail.ru
·
01.01.1970 03:00
nnWhat is the purpose of
n__slots__in Python. Especially regarding when to use it and when not to?
The special attribute __slots__ allows you to explicitly specify which instance attributes you expect from instances of your object, with the expected results.:
Space is saved because
nReferences are saved to the value of slots and not in __dict__
Prohibiting the creation of __dict__ and __weakref__, if the parent classes forbid them, and you declare __slots__.
In the inheritance tree, you only need to declare a specific slot once
nFor example:
nclass/span> Base:n __slots__ = 'foo', 'bar'nclass/span>nclass">class Right(Base):n __slots__ = 'baz',n nclass/span> Wrong">Wrong(Base):n __slots__ = 'foo', 'bar', # redundant foo and barnnPython doesn't mind when you make mistakes (it probably should), otherwise the problems may not show up, but your objectsobjects will take up more space than they should.
nPython 3.8:
n>>> from sys import getsizeofn>n>>> getsizeof(Right()), getsizeof(Wrong())n(56, 7272) nnThis is because the Base slot descriptor has a slot separate from Wrong.
>>;>> w = Wrong()n>>> w.foo = 'foo'n>>> Base.foo.__get__(w)nTraceback (most recent call last):n File ""<stdin>"", line 1, in <modulein <module>nAttributeError: foon>>> Wrong.foo.__get__(w)n'foo''foo'nnThe biggest caveat conces multiple inheritance - multiple "parent classes with non-empty slots"; they cannot be combined.
nTo circumvent this limitation, follow the recommendations: Isolate all abstractions of the parents, except for one or all of which their specific class will inherit from, and your new class by giving the abstractions empty slots (just like the abstract base classes in the standard library)
nSee See the example below in the section on multiple inheritance.
nRequirements:
nSo that attributes named in __slots__ are stored in slots and not in __dict__, class must inherit from object.
To prevent the creation of __dict__, you must inherit from objectw, and all classes in inheritance must declare __slots__, and none of them can have an entry __dict__.
If you want to continue reading, there are many more details.
n__slots__: faster access to attributes.The creator of Python, Guido van Rossum, says that he actually created __slots__ for faster access to attributes.
Demonstrate faster access - this is a trivial task:
nimport timeitnclass>class Foo(object): __slots__ = 'foo',n,nclass Bar(object): passnslotted = Fonslotted = Foo()nnot_slotted = Bted = Bar()ndef get_set_delete_fn(obj):n def/span> get_set_delete():n &nbssp; obj.foo = 'foo'n del obj.foon retu get_set_deletennand
n>>> min(timeit.repeat(get_set_delete_fn(slotted)))n0.2846834 class="">0.2846834529991611n>>> min(timeit.repeat(get_set_delete_fn(not_slotted)))n0.3664822799983085 n nn> nn
In Python 3.5 on Ubuntu, slot access is almost 30% faster.
n>>> 0.3664822799983085 / 0.2846834529991611n1.2873325658284342 nn42 nn
I measured this in Python 2 on Windows, which tued out to be about 15% faster.
n__slots__: save memoryAnother goal __slots__ - reduce the amount of memory that each instance of the object occupies.
nnThe space saved by using
n__dict__can be significant.
attributes significant memory savings to __slots__.
To test this using the Anaconda distribution for Python 2.7 on Ubuntu Linux with guppy.hpy (aka heapy) and sys.getsizeof, the size of the class instance without the declared __slots__ and nothing else is 64 bytes. This does not include __dict__. Thanks again to Python for the lazy evaluation, __dict__ is apparently not called until it is referenced, but classes without data are usually useless. When called, the __dict__ attribute has an additional minimum of 280 bytes.
On the contrary, an instance of a class with __slots__ declared as () (without data), is only 16 bytes, and only 56 bytes with one element in slots, 64 with two.
For 64-bit Python, I will illustrate the memory consumption in bytes in Python 2.7 and 3.6 for __slots__ and __dict__ (slots are not defined) for each point where dict increases by 3.6 (except attributes 0, 1 and 2):
Python 2.7Python 3.6span>attrs__slots_____dict__*__slots____dict__* | *(slots not defined)s not defined)n none 16 56 + 272† 16 56="">56 + 112† | dagger;if __dict__ mentions one, 4856 + 272 48 &n 56 + 112n two 56 56/span> + 272 56 56p; 56 + 112n six 88 &nbnbsp; 56 + 1040 88 p; 56 + 152n 11 128 56="">56 + 1040 128 56 + 240n 22 216 &n; 56 + 3344 216 56/span> + 408 n 43 384 56sp; 56 + 3344 384