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