How to use data descriptors in Python

Key takeaways

  • A data descriptor is a type of descriptor that implements the __set__() or __delete__() methods.

  • Data descriptors manage how attributes are set or deleted, overriding default behavior when accessed through an instance.

  • Common uses include validation, type checking, access logging, triggering actions on access/modification, and caching results for performance.

Descriptors in Python are objects that define how attribute access is handled for objects. They provide a way to customize attribute access on their classes by defining methods for getting, setting, or deleting attributes. A data descriptor is a type of descriptor that includes the __set__(), or __delete()__ methods; these methods are used to remove an attribute’s value or assign a new value to it.

Descriptors: Intermediaries in Python classes

Descriptors can be thought of as intermediate objects between the class definition and its instance. When an attribute gets accessed from an instance of a class containing descriptors, the descriptor intervenes before returning or setting the actual value.

class DataDescriptor:
def __set__(self, obj, value):
print("Setter function of the descriptor class invoked.")
obj._amount = value
def __get__(self, obj, value):
print("Getter function of the descriptor class invoked.")
return obj._amount
class Animal:
name= "Fluffy"
age = DataDescriptor()
my_pet = Animal()
my_pet.age = 5
print("Age:", my_pet.age)
  • Line 1: We start the definition of a class named DataDescriptor. This class is intended to serve as a data descriptor, providing custom behavior for attribute access.

  • Lines 2–4: We define the __set__ method within the DataDescriptor class. This method is called when the attribute associated with this descriptor is assigned a new value. It takes three parameters:

    • self: This is the instance of the descriptor itself.

    • obj: This is the instance of the class owning the attribute.

    • value: This is the new value being assigned to the attribute. The method assigns the new value to the _amount attribute of the instance obj.

  • Lines 6–8: We define the __get__ method within the DataDescriptor class. This method is called when the attribute associated with this descriptor is accessed. It takes the same three parameters as __set__ and returns the current value of the _amount attribute of the instance obj.

  • Lines 10–12: We define a class named Animal. It has two class attributes:

    • name is initialized to the string "Fluffy".

    • age is initialized to an instance of the DataDescriptor class. This means that age is a descriptor attribute, and its behavior for setting and getting values is defined by the DataDescriptor class.

  • Line 14: We create an instance of the Animal class named my_pet.

  • Line 16: We assign the value 5 to the name attribute of the my_pet instance. As age is a descriptor attribute with a custom __set__ method defined in the DataDescriptor class, this assignment invokes the __set__ method of the DataDescriptor class, which sets the value of _amount attribute in the my_pet instance to 5.

  • Line 17: We print the value of the age attribute of the my_pet instance. As age is a descriptor attribute with a custom __get__ method defined in the DataDescriptor class, this access invokes the __get__ method of the DataDescriptor class, which returns the current value of the _amount attribute in the my_pet instance, which is 5.

Data descriptor for validating attribute values

Validation in data descriptors ensures data integrity and consistency by enforcing rules on data entries, preventing errors, and enhancing security. It provides immediate user feedback, simplifies debugging and maintenance, and ensures compliance with regulatory standards, ultimately improving software reliability and user experience.

Now let’s try to add validation checks in the setter function.

class DataDescriptor:
def __set__(self, obj, value):
if value < 0 or value > 25:
raise ValueError("Please enter correct age.")
obj._amount = value
def __get__(self, obj, value):
return obj._amount
class Animal:
name= "Fluffy"
age = DataDescriptor()
my_pet = Animal()
# Try changing the age outside the given range.
my_pet.age = 2
print("Age:", my_pet.age)

This code focuses on validating the assigned value for the age attribute within the DataDescriptor class. In the __set__ method of the DataDescriptor class, it checks whether the value being assigned to the attribute falls within the specified range (0 to 25). If the value is outside this range, it raises a ValueError with the message “Please enter correct age.”. The validation checks ensure that only valid ages are accepted for the age attribute, providing robust data integrity and preventing incorrect values from being set.

Use cases

Let’s look at some use cases for data descriptors.

  • Validation: Data descriptors can be used to validate the values of attributes before they are set by the user. For instance, consider an attribute representing the age of a person, which should always be a positive integer; a descriptor can be defined to raise an exception if a negative value is given.

  • Type checking: A descriptor can be defined to ensure that the value assigned to an attribute is of a specific type. For example, in a movie playlist class, a data descriptor ensures that the assigned genre is a string, preventing unintended data types.

  • Access logging: Data descriptors can serve the purpose of access logging, providing insights into when and how attributes are accessed. This can be valuable for monitoring and analyzing the usage patterns of specific attributes within a program.

  • Action triggering: Data descriptors offer the capability to trigger specific actions when attributes are accessed or modified. This functionality can be harnessed to automate responses, execute pre or postprocessing tasks, or enforce particular behaviors based on attribute interactions.

  • Caching: For instance, we have an expensive computation or calculation associated with an attribute; we can define a descriptor that stores the result of the computation so it doesn’t need to be recalculated each time it is accessed.

These diverse applications highlight the adaptability of data descriptors in addressing various challenges related to attribute management within a program.

Free Resources

Copyright ©2025 Educative, Inc. All rights reserved