Python Descriptor

Descriptor is a class that can be initialized as class variable in anthoer class definition. This descriptor is capable of setting __get__ and __set__ methods to provide additioanl operations on the attribtues when getting and setting values for the instance.

Refer to official documentation for a very good example of implementing a validator using the descriptor.

A descriptor is initialized whenever the class is defined.

class Grade:
    def __init__(self) -> None:
        print("__init__ is called")

    def __set_name__(self, owner, name):
        print(f"__set_name__ is called with {owner.__name__} on attribute {name}")
        self._private_name = f"_{name}"

    def __get__(self, instance, instance_type):
        print(f"__get__ is called")
        return getattr(instance, self._private_name)

    def __set__(self, instance, value):
        print(f"__set__ is called")
        setattr(instance, self._private_name, value)

class Exam:
    grade = Grade()
    def __init__(self, grade):
        self.grade = grade
__init__ is called
__set_name__ is called with Exam on attribute grade

Note that when the instance attribute grade is set a value, the descriptor sets the value on the private attribute _grade for this instance. Only _grade belongs to this instance. Why? This is because the grade attribute is a class variable that all the instances will share. To make the grade a “deep copy” for every instance, and only acts as an interface, the real value is embedded in the private attribute.

instance = Exam(grade=100)
print(instance.__dict__)
__set__ is called
{'_grade': 100}

grade can still be called on a instance level, though under the screen this is returning the _grade.

print(instance.grade)
__get__ is called
100
Yiming Zhang
Yiming Zhang
Quantitative Researcher Associate, JP Morgan