Non-data descriptors

We will start with a descriptor that only implements the __get__ method, and see how it is used:

class NonDataDescriptor:
def __get__(self, instance, owner):
if instance is None:
return self
return 42


class ClientClass:
descriptor = NonDataDescriptor()

As usual, if we ask for the descriptor, we get the result of its __get__ method:

>>> client = ClientClass()
>>> client.descriptor
42

But if we change the descriptor attribute to something else, we lose access to this value, and get what was assigned to it instead:

>>> client.descriptor = 43
>>> client.descriptor
43

Now, if we delete the descriptor, and ask for it again, let's see what we get:

>>> del client.descriptor
>>> client.descriptor
42

Let's rewind what just happened. When we first created the client object, the descriptor attribute lay in the class, not the instance, so if we ask for the dictionary of the client object, it will be empty:

>>> vars(client)
{}

And then, when we request the .descriptor attribute, it doesn't find any key in client.__dict__ named "descriptor", so it goes to the class, where it will find it ... but only as a descriptor, hence why it returns the result of the __get__ method.

But then, we change the value of the .descriptor attribute to something else, and what this does is set this into the dictionary of the instance, meaning that this time it won't be empty:

>>> client.descriptor = 99
>>> vars(client)
{'descriptor': 99}

So, when we ask for the .descriptor attribute here, it will look for it in the object (and this time it will find it, because there is a key named descriptor in the __dict__ attribute of the object, as the vars result is showing us), and return it without having to look for it in the class. For this reason, the descriptor protocol is never invoked, and the next time we ask for this attribute, it will instead return the value we have overridden it with (99).

Afterward, we delete this attribute by calling del, and what this does is remove the key "descriptor" from the dictionary of the object, leaving us back in the first scenario, where it's going to default to the class where the descriptor protocol will be activated:

>>> del client.descriptor
>>> vars(client)
{}
>>> client.descriptor
42

This means that if we set the attribute of the descriptor to something else, we might be accidentally breaking it. Why? Because the descriptor doesn't handle the delete action (some of them don't need to).

This is called a non-data descriptor because it doesn't implement the __set__ magic method, as we will see in the next example.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.184.113