A descriptor is a class that implements the __get__, __set__, and __del__ methods, which can be further subdivided into two categories:
Data descriptors: implements __get__ and __set__
Non-data descriptors: no implementation of __set__
Descriptors play a very important role in the property calls of a class, and classes follow two rules when calling properties:
Select attributes in the order of instance attributes, class attributes, i.e., instance attributes take precedence over class attributes
If a data descriptor with the same name is found in a class attribute, then that descriptor takes precedence over the instance attribute
Non-data descriptors are overridden by instance attributes
class A: def __get__(self, obj, cls): return f"{obj}: get" class B: value = A() def __init__(self): = 4 def main(): g = B() print() print(g.__dict__) if __name__ == "__main__": main()
output result
4
{'value': 4}
Data descriptors are preferred over instance attributes
class A: def __get__(self, obj, cls): return f"{obj}: get" def __set__(self, obj, value): print(f"{obj}: set, {value}") class B: value = A() def __init__(self): = 4 def main(): g = B() print() print(g.__dict__) if __name__ == "__main__": main()
output result
<__main__.B object at 0x000001165EB85898>: set, 4
<__main__.B object at 0x000001165EB85898>: get
{}
As you can see from the above two examples, the value attribute of class B is a descriptor. When the value attribute is a data descriptor, it masks the instance's attribute of the same name, value, and the instance's reads and assignments to the value attribute are transferred directly to the class attribute, value.
Implementing Class Static Methods and Class Methods Using Descriptors
from functools import partial class Staticmethod: def __init__(self, method): = method def __get__(self, obj, cls): return class Classmethod: def __init__(self, method): = method def __get__(self, obj, cls): return partial(, cls) class A: @Staticmethod def f(self): print(f"I'm method f, the value is {self}") @Classmethod def c(self): print(f"my class is {self}") a = A() (23) (23) () ()
output result
I'm method f, the value is 23
I'm method f, the value is 23
my class is <class '__main__.A'>
my class is <class '__main__.A'>
Static methods and class methods unify the two ways of referring to class attributes. This unification can be achieved by using descriptors to modify the default way properties are accessed. Static methods restrict the default binding of instances and use methods as normal functions; class methods always pass the class as the first parameter, and the above mentioned partial fixes the class as the first parameter of the method.
summarize
- Descriptors are classes that implement special methods such as __get__, __set__, __del__, etc., which play a big role in property access.
- A data descriptor overrides an instance attribute of the same name, and by using a data descriptor, you achieve the goal of modifying class variables by instance.
- The descriptor is used to modify the default access to the attribute, which enables the implementation of class methods and static methods.
This is the whole content of this article, I hope it will help you to learn more.