2 min read

Python Descriptors

Descriptors are Python objects that implement a method of the descriptor protocol, which gives you the ability to create objects that have special behavior when they’re accessed as attributes of other objects.

The Descriptor Protocol

A descriptor is an object that has any of the following methods:

  • __get__(self, obj, type=None): Called to get the attribute.
  • __set__(self, obj, value): Called to set the attribute.
  • __delete__(self, obj): Called to delete the attribute.

If an object defines __get__ and __set__, it is considered a data descriptor. If it defines only __get__, it is a non-data descriptor.

Creating a Descriptor

Here is a simple example of a descriptor that logs access to an attribute.

class VerboseAttribute:
    def __init__(self, name):
        self.name = name

    def __get__(self, obj, objtype=None):
        print(f"Getting {self.name}")
        return obj.__dict__.get(self.name)

    def __set__(self, obj, value):
        print(f"Setting {self.name} to {value}")
        obj.__dict__[self.name] = value

class MyClass:
    # The descriptor is assigned to a class attribute
    attribute = VerboseAttribute("my_attribute")

m = MyClass()
m.attribute = 10  # Calls __set__
print(m.attribute) # Calls __get__

property()

The built-in property() function is actually a way to create a descriptor.

class C:
    def __init__(self):
        self._x = None

    @property
    def x(self):
        return self._x

    @x.setter
    def x(self, value):
        self._x = value

Use Cases

Descriptors are the mechanism behind properties, methods, static methods, class methods, and super(). They are used throughout Python itself to implement these features.

programming/python/python