Reputation: 2608
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?
Upvotes: 1
Views: 252
Reputation: 152795
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
.
Upvotes: 4
Reputation: 532218
Consider a simple class:
class A:
__slots__ = ('a',)
What is a
? It's a descriptor:
>>> type(A.a)
<class 'member_descriptor'>
Each string in the value of __slots__
is used to create a class attribute by that name with a member_descriptor
value.
That means you can (try to) access it via A.a.__get__
>>> a = A()
>>> a.a
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: a
assign to it with A.a.__set__
>>> a.a = 7
and try to access it again :)
>>> a.a
7
What you can't do is try to assign to any other attribute on the instance:
>>> A.b
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: type object 'A' has no attribute 'b'
>>> a.b
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'A' object has no attribute 'b'
>>> a.b = 0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'A' object has no attribute 'b'
The presence of __slots__
not only creates the requested class attributes, but prevents the creation of any additional attributes on an instance.
Upvotes: 3