Understanding Python’s Descriptor Protocol

Database fields rely on Python’s Descriptor Protocol to manage data, and built-in properties like @property and staticmethod use it to control attribute access. Descriptors allow developers to define custom behavior for attributes, ensuring better control over how values are stored, retrieved, and modified.

What are Descriptors?

A descriptor is a Python object that controls attribute access through special methods:

__get__(self,instance,value) defines behavior when retrieving an attribute.

__set__(self,instance,value) Defines behavior when assigning a value.

__delete__(self,instance,value) Defines behavior when deleting an attribute.

By implementing these methods, we can customize how attributes work inside a class.

Why use descriptors?

  • Encapsulation – Hide internal logic and enforce rules.
  • Code Reusability – Create reusable attribute management logic.
  • Validation – Ensure only valid data is stored.

Descriptors are used in ORMs, logging, caching, and computed properties, making Python more flexible.

Creation

Here’s and example of creating and using one:

class PositiveNumber:
    def __get__(self, instance, owner):
        return instance.__dict__.get(self.name, 0)

    def __set__(self, instance, value):
        if value < 0:
            raise ValueError("Value must be positive!")
        instance.__dict__[self.name] = value

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

class Product:
    price = PositiveNumber()  #descriptor

p = Product()
p.price = 100  
print(p.price)  #100

p.price = -5  # ValueError value should be positive

PositiveNumber ensures that price only stores positive values, preventing invalid data entry.

@property

lets us define getter, setter, and deleter methods inside a class:

class Temperature:
    def __init__(self, celsius):
        self._celsius = celsius

    @property
    def celsius(self):
        return self._celsius

    @celsius.setter
    def celsius(self, value):
        if value < -273.15:
            raise ValueError("Temperature cant be below zero")
        self._celsius = value

temp = Temperature(25)
print(temp.celsius)  # 25
temp.celsius = -300  # ValueError

When

  • If there are multiple classes which need common attribute logic (ex: validation).
  • When you need advanced control over attribute behavior.
  • While using ORMs, caching, logging, or computed properties.
          1. ORMs let you interact with databases using objects instead of writing raw SQL, making data management easier.
          2. Caching stores frequently used data in memory so it doesn’t have to be reloaded every time, making programs run faster.
          3. Logging: Logging records events like errors, updates, or function calls in a program, helping with debugging and tracking activity.
          4. Computed Properties: These are attributes that calculate their value on the fly instead of storing it, saving memory and keeping data up to date.

Python’s Descriptor Protocol gives developers control over attribute access, making code cleaner, safer, and reusable. While @property works well for basic cases, descriptors allows more customization for complex applications.

So, next time you need custom attribute behavior, think beyond @property and try descriptors.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top