On the priority of (data descriptor) and instance properties of data-type descriptors

python.org/3/howto/descriptor.html-sharpdefinition-and-introduction" rel=" nofollow noreferrer "here the official document of describes what happens in the form of a.b :

will first try a.invalid dict__ _ ["b"] , then type (a). _ _ dict__ ["b"] if it is not found, and try it one by one in mro order until the unknown is found. And after finding it, check to see if b here defines a descriptor-related method _ get__ / _ set__ , and if so, replace the default behavior with descriptor-related methods.

has the following simple example:

class Descri(object):
    def __get__(self, obj, type=None):
        print("call get")
    
    def __set__(self, obj, value):
        print("call set")

class A(object):
    x = Descri()

a = A()
a.__dict__["x"] = 1

after the last line is executed, we get a.x .
according to the search order mentioned earlier, the x attribute should be found directly in the _ _ dict__ of the instance a , and it is equal to 1 and no special method is defined, so 1 should be returned directly according to the search priority, and you should not continue to look for any type (a). _ dict__ . But this is not the case. In fact, the Descri.__get__ method is still called.
excuse me, why is this?

Mar.25,2021

thanks for the invitation, a.x is hit in the variable space of the instance property, so it won't look in the variable space of the parent class. In my test, there is no call to Descri.__get__ :

the local version 3.6 has the same result.

= split line =
try a.x on the last line as the subject said, and you did execute Descri.__get__ . This weird sequence of calls is actually explained in the documentation.

access to a property has been overridden by the descriptor protocol method: _ _ get__ () , _ _ set__ () , and _ _ delete__ () . If any of these methods are defined for an object, they are called descriptors.

in general (I mean in general), the search chain for a.x is first a.invalid searching dict__ _ ['x'] , followed by type (a). _ dict__ ['x'] , and if not found, continue to search through the base class of type (a) .

however, if one of the values you are looking for is an object that defines a descriptor method, then Python overrides this default behavior and invokes the descriptor method. This behavior will also vary slightly depending on the call:

  • if the call is an object instance (the invocation method in the title), a.x is converted to calling:. type (a). _ _ dict__ ['x']. _ _ get__ (a, type (a))
  • if you are calling a class attribute, A.x is converted to: A.class attributes called _ ['x']. _ _ get__ (None, A)
  • other situations

see the documentation for more references: python.org/3/reference/datamodel.html-sharpimplementing-descriptors" rel=" nofollow noreferrer "> python.org/3/reference/datamodel.html-sharpimplementing-descriptors" rel= "nofollow noreferrer" > https://docs.python.org/3/ref.


Let's start with the classification of descriptors:

    The
  1. data descriptor
    implements at least _ _ set__ and _ _ get__
  2. non-data descriptor
    only implements _ _ get__

then the access priority of the following descriptor:
class. Attribute > data descriptor > object. Attribute > non-data descriptor

when you use a. Characters written to _ ['x'] = 1, {'xletters: 1} is written to the namespace of a. But when you access with a.x, because you implement a data descriptor, it takes precedence over the object. Property, type (a). _ _ dict__ ['x']. _ _ get__ is executed instead of a.

if you want to execute a.x, return a. Collect recent requests _ ['x']. Just comment out the _ _ set__ above. Because the non-data description is implemented at this time, the priority is less than the object. Property.
I hope it helps you!

Menu