DEV Community

Cover image for Descriptor Protocol and how ORM uses it?
Sumit Roy
Sumit Roy

Posted on • Edited on

Descriptor Protocol and how ORM uses it?

What is descriptor?

(Of course, some geeky definition coming)

According to official python3 docs, this is what they are saying

In general, a descriptor is an object attribute with “binding behavior”, one whose attribute access has been overridden by methods in the descriptor protocol. Those methods are __get__(), __set__(), and __delete__(). If any of those methods are defined for an object, it is said to be a descriptor.

Now it seems pretty clear that we need a class and and in that class we need to define these methods and that's it. Let's try out

class A:
    def __get__(self, instance, owner):
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        print("setting the attribute")
        instance.__dict__[self.name] = value

    def __delete__(self, instance):
        del instance.__dict__[self.name]

    def __set_name__(self, owner, name):
        self.name = name
Enter fullscreen mode Exit fullscreen mode

Now we want to use this descriptor

class B:
    a = A()
i = B()
print(i.a)
# KeyError: 'a'
i.a = "wysiwyg"
# setting the attribute
print(i.a)
# wysiwyg
Enter fullscreen mode Exit fullscreen mode

Use cases

(Okay, enough chit-chat let's fight, sorry sorry let's understand how we can use this concept)
So suppose you want validation before setting a value or getting the value. But wait there are getters and setters, right? Yes, there are but I am a fan of DRY and I solemnly swear to not repeat except necessary. So even if the condition is same you have to use setter each time for each attribute.

class CheckString:
    def __set_name__(self, owner, name):
        self.name = name

    def __set__(self, instance, value):
        if isinstance(value, str):
            instance.__dict__[self.name] = value
        else:
            raise ValueError("Value must be string")


class User:
    name = CheckString()
    email = CheckString()

    def __init__(self, name, email):
        self.name = name
        self.email = email

u = User("Sumit", 1)
# ValueError: Value must be string

Enter fullscreen mode Exit fullscreen mode

Now if we want to implement the same with property how we would have

class User:
    def __init__(self, name, email):
        self._name = name
        self._email = email

    @property
    def email(self):
        return self._email

    @email.setter
    def email(self, value):
        if isinstance(value, str):
            self._email = value
        else:
            raise ValueError("Value must be string")

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, value):
        if isinstance(value, str):
            self._name = value
        else:
            raise ValueError("Value must be string")

...

u.email = 1
# ValueError: Value must be string
Enter fullscreen mode Exit fullscreen mode

In the next post, we will try to decode how ORM(Object-Relational Mapping) take advantage of descriptors.

Liked my post?

Top comments (2)

Collapse
 
pallabganguly profile image
Pallab Ganguly

Great post! Where’s part 2?

Collapse
 
sroy8091 profile image
Sumit Roy

Served.