Search code examples
pythondescriptorpython-descriptors

Understanding Python Descriptors


I am trying to understand descriptors better.

I don't understand why in the foo method the descriptors __get__ method doesn't get called.

As far as I understand descriptors the __get__ method always get called when I access the objects attribute via dot operator or when I use __getattribute__().

According to the Python documentation:

class RevealAccess(object):
    def __init__(self, initval=None, name='var'):
        self.val = initval
        self.name = name

    def __get__(self, obj, objtype):
        print('Retrieving', self.name)
        return self.val

    def __set__(self, obj, val):
        print('Updating', self.name)
        self.val = val

class MyClass(object):
    x = RevealAccess(10, 'var "x"')
    y = 5

    def foo(self):
        self.z = RevealAccess(13, 'var "z"')
        self.__getattribute__('z')
        print(self.z)

m = MyClass()
m.foo()
m.z # no print
m.x # prints var x

Solution

  • z is an attribute on the instance, not on the class. The descriptor protocol only applies to attributes retrieved from a class.

    From the Descriptor HOWTO:

    For objects, the machinery is in object.__getattribute__() which transforms b.x into type(b).__dict__['x'].__get__(b, type(b)).

    and in the Implementing Descriptors section of the Python Data Model:

    The following methods only apply when an instance of the class containing the method (a so-called descriptor class) appears in an owner class (the descriptor must be in either the owner’s class dictionary or in the class dictionary for one of its parents).

    Your m.z cannot be found in the class dict; type(m).__dict__['z'] does not exist; it is found in m.__dict__['z'] instead. Here m is the instance and the owner class is MyClass, and z does not appear in the owner class dictionary.