SoFunction
Updated on 2024-11-21

Python descriptor descriptor use principle analysis

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.