Search code examples
pythonpython-3.xfunctionclassbuilt-in

How does __getattribute__ fetch a value?


class test():
  name = 'arthur'

  def __getattribute__(self, value):
    return super().__getattribute__(value)

x = test()
x.name

------------------------------------------
Output: 'arthur'

I'm trying to understand the underlying mechanism of __getattribute__. I understand (hopefully) that super().__getattribute__(value) reaches out to the object class in this case. But how actually does object fetch the name value out of the test() class? And how could one fetch an attribute value with __getattribute__ if coded in a class by oneself while avoiding recursion?

As I said, I want to understand the underlying mechanisms, I know this isn't the way how you would usually handle things.

Thanks!


Solution

  • First of all, the __getattribute__ is a magic method (aka dunder method/double underscore method). This is a property/attribute accessor method and it intercepts on every property/attribute access even if the property/attribute is available in the class itself. On the other hand, the __getattr__ magic method is called only if the property you are accessing doesn't exist in the class/instance. Anyways...

    There is also one more important thing to notice that, as you might already know that, all classes extends/inherits the base object class implicitly or explicitly. So, any class you define, the default parent class is the built-in object class. You are confused as you asked:

    I understand (hopefully) that super().__getattribute__(value) reaches out to the object class in this case. But how actually does object fetch the name value out of the test() class?

    So lets see some basic examples first:

    class Foo:
        def __init__(self, name):
            self.name = name
    
        def __getattribute__(self, name):
            return super().__getattribute__(name)
    

    is equivalent to

    class Foo(object):
        def __init__(self, name):
            self.name = name
    
        def __getattribute__(self, name):
            return object.__getattribute__(self, name)
    

    So, when you call

    object.__getattribute__(self, name)
    

    You are passing the context (an instance of the class) explicitly to the parent (object) class. so the parent/object class knows the context and get the attribute from that passed instance. On the other hand, when you call:

    super().__getattribute__(name)
    

    Python sets the context for you. So, you can imagine something like this:

    super(test, self).__getattribute__(name) # it's valid
    

    But in this case, it's implicit, that's how the super() call knows where to lookup for the attribute. It's worth mentioning that, Python's super() builtin function returns a proxy object, a substitute object that can call the method of the base class via delegation and also super() can take two parameters (as you've seen in previous code snippet), the first is the subclass type (in this case test), and the second parameter is an object, an instance of that subclass (test).

    In Python 3, the super(test, self) call is equivalent to the parameterless super() call. Hope I've clarified your confusions. You may read more about super().