简体   繁体   中英

Why the introduction of __slots__ was made possible with descriptors?

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 .

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 Aa__get__

>>> a = A()
>>> a.a
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: a

assign to it with Aa__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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM