аватар question@mail.ru · 01.01.1970 03:00

What is the goal of __slots__ in Python?

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
n

What is the purpose of __slots__ in Python. Especially regarding when to use it and when not to?

n
n

TLDR:

n

The special attribute __slots__ allows you to explicitly specify which instance attributes you expect from instances of your object, with the expected results.:

n
    n
  1. quick access to attributes.
  2. n
  3. saving memory.
  4. n
n

Space is saved because

n
    n
  1. n

    References are saved to the value of slots and not in __dict__

    n
  2. n
  3. n

    Prohibiting the creation of __dict__ and __weakref__, if the parent classes forbid them, and you declare __slots__.

    n
  4. n
n

Brief waings

n

In the inheritance tree, you only need to declare a specific slot once

n

For example:

n
class/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', 'baz' # redundant foo and barn
n

Python 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.

n

Python 3.8:

n
>>> from sys import getsizeofn>n>>> getsizeof(Right()), getsizeof(Wrong())n(56, 7272) n
n

This is because the Base slot descriptor has a slot separate from Wrong.

n
>>;>> 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'n
n

The biggest caveat conces multiple inheritance - multiple "parent classes with non-empty slots"; they cannot be combined.

n

To 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)

n

See See the example below in the section on multiple inheritance.

n

Requirements:

n
    n
  1. n

    So that attributes named in __slots__ are stored in slots and not in __dict__, class must inherit from object.

    n
  2. n
  3. n

    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__.

    n
  4. n
n

If you want to continue reading, there are many more details.

n

Why use __slots__: faster access to attributes.

n

The creator of Python, Guido van Rossum, says that he actually created __slots__ for faster access to attributes.

n

Demonstrate faster access - this is a trivial task:

n
import 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_deleten
n

and

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
n

n> nn

In Python 3.5 on Ubuntu, slot access is almost 30% faster.

n
>>> 0.3664822799983085 / 0.2846834529991611n1.2873325658284342 n
n

42 nn

I measured this in Python 2 on Windows, which tued out to be about 15% faster.

n

Why use __slots__: save memory

n

Another goal __slots__ - reduce the amount of memory that each instance of the object occupies.

n

n
n

The space saved by using __dict__ can be significant.

n
n

attributes significant memory savings to __slots__.

n

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.

n

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.

n

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):

n
               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                                     

Latest

Similar