GarlicPasta GarlicPasta - 1 month ago 6
Python Question

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

Answer

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.