DEV Community

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

Posted on • Edited on

1 1

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?

Heroku

Simplify your DevOps and maximize your time.

Since 2007, Heroku has been the go-to platform for developers as it monitors uptime, performance, and infrastructure concerns, allowing you to focus on writing code.

Learn More

Top comments (2)

Collapse
 
pallabganguly profile image
Pallab Ganguly

Great post! Where’s part 2?

Collapse
 
sroy8091 profile image
Sumit Roy

Served.

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay