# 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.

```python
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.

```python
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]]