In this blog post from the series "The History of Python", Guido van Rossum states:
Another enhancement made possible with descriptors was the introduction of the __slots__ attribute on classes.
I understand this sentence as: under the hood __slots__ is implemented by descriptors.
But contrary to this my interpretation, Guido van Rossum writes a few lines later:
Underneath the covers, the implementation of this feature is done entirely in C and is highly efficient.
So, __slots__ is not implemented by descriptors?
But two sentences later, again he writes:
Not only was __slots__ an interesting application of descriptors, ...
So, what's actually the case with __slots__ and descriptors?
Is __slots__ implemented by descriptors or not? And if yes: how?
The statements don't contradict themselves. The attributes defined by __slots__
are descriptors on the created class and the implementation of that descriptor is written in C (assuming CPython).
The descriptor class is called member_descriptor
as can be seen by this example code:
import inspect
class Test:
__slots__ = 'a',
def __init__(self, a):
self.a = a
type(Test.a) # member_descriptor
inspect.isdatadescriptor(Test.a) # True
inspect.ismemberdescriptor(Test.a) # True
And a quick search on the CPython repository on GitHub revealed the C implementation of it (Link for CPython version 3.8.0).
To go into a bit more detail:
A Python class is essentially a dict
with (a lot of) bells and whistles. On the other hand there are Python-C-classes that use a C-struct
to implement a Python class. Such a C-struct is faster and requires (significantly) less memory than a dictionary even if it only contains Python objects (which is basically a C-array containing references to Python objects).
To make it possible that "normal" Python classes could benefit from the faster access and the reduced memory footprint __slots__
was introduced. A class with __slots__
will be translated essentially to a C-struct. However to make it possible that the attribute lookup/setting/deletion map to the corresponding struct
member some sort of translation layer (descriptors) is required. That translation layer for members defined in __slots__
is member_descriptor
.
So when you look up the attribute on an instance of a __slots__
-class you'll get a member_descriptor
and that member_descriptor
will know how to get/set/delete the member of the underlying C-struct
.