Search code examples
pythonpython-2.7classgetattr

Use of getattr on a class with data descriptor


While using data descriptors in building classes, I came across a strange behavior of getattr function on a class.

# this is a data descriptor
class String(object):
    def __get__(self, instance, owner):
        pass
    def __set__(self, instance, value):
        pass

# This defines a class A with 'dot' notation support for attribute 'a'
class A(object):
    a = String()

obj = A()
assert getattr(A, 'a') is A.__dict__['a']
# This raises AssertionError

LHS return an empty string, while the RHS returns an instance of String. I thought getattr on an object was to get the value for the key inside the __dict__. How does getattr function work on a class object?


Solution

  • getattr(A, 'a') triggers the descriptor protocol, even on classes, so String.__get__(None, A) is called.

    That returns None because your String.__get__() method has no explicit return statement.

    From the Descriptor Howto:

    For classes, the machinery is in type.__getattribute__() which transforms B.x into B.__dict__['x'].__get__(None, B).

    getattr(A, 'a') is just a dynamic from of A.a here, so A.__dict__['x'].__get__(None, A) is executed, which is why you don't get the same thing as A.__dict__['x'].

    If you expected it to return the descriptor object itself, you'll have to do so explicitly; instance will be set to None in that case:

    class String(object):
        def __get__(self, instance, owner):
            if instance is None:
                return self
        def __set__(self, instance, value):
            pass
    

    This is what the property descriptor object does.

    Note that the owner argument to descriptor.__get__ is optional; if not set you are supposed to use type(instance) instead.