__delete__(self, instance)

This method is called upon with the following statement, in which self would be the descriptor attribute, and instance would be the client object in this example:

>>> del client.descriptor

In the following example, we use this method to create a descriptor with the goal of preventing you from removing attributes from an object without the required administrative privileges. Notice how, in this case, that the descriptor has logic that is used to predicate with the values of the object that is using it, instead of different related objects:

# descriptors_methods_3.py

class ProtectedAttribute:
def __init__(self, requires_role=None) -> None:
self.permission_required = requires_role
self._name = None

def __set_name__(self, owner, name):
self._name = name

def __set__(self, user, value):
if value is None:
raise ValueError(f"{self._name} can't be set to None")
user.__dict__[self._name] = value

def __delete__(self, user):
if self.permission_required in user.permissions:
user.__dict__[self._name] = None
else:
raise ValueError(
f"User {user!s} doesn't have {self.permission_required} "
"permission"
)


class User:
"""Only users with "admin" privileges can remove their email address."""

email = ProtectedAttribute(requires_role="admin")

def __init__(self, username: str, email: str, permission_list: list = None) -> None:
self.username = username
self.email = email
self.permissions = permission_list or []

def __str__(self):
return self.username

Before seeing examples of how this object works, it's important to remark some of the criteria of this descriptor. Notice the User class requires the username and email as mandatory parameters. According to its __init__ method, it cannot be a user if it doesn't have an email attribute. If we were to delete that attribute, and extract it from the object entirely we would be creating an inconsistent object, with some invalid intermediate state that does not correspond to the interface defined by the class User.  Details like this one are really important, in order to avoid issues. Some other object is expecting to work with this User, and it also expects that it has an email attribute.

For this reason, it was decided that the "deletion" of an email will just simply set it to None, and that is the part of the code listing that is in bold. For the same reason, we must forbid someone trying to set a None value to it, because that would bypass the mechanism we placed in the __delete__ method.

Here, we can see it in action, assuming a case where only users with "admin" privileges can remove their email address:

>>> admin = User("root", "[email protected]", ["admin"])
>>> user = User("user", "[email protected]", ["email", "helpdesk"])
>>> admin.email
'[email protected]'
>>> del admin.email
>>> admin.email is None
True
>>> user.email
'[email protected]'
>>> user.email = None
...
ValueError: email can't be set to None
>>> del user.email
...
ValueError: User user doesn't have admin permission

Here, in this simple descriptor, we see that we can delete the email from users that contain the "admin" permission only. As for the rest, when we try to call del on that attribute, we will get a ValueError exception.

In general, this method of the descriptor is not as commonly used as the two previous ones, but it is worth showing it for completeness.

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

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