Search code examples
pythongetattributepython-descriptors

Why cls.__getattribute__ returns a function instead of a bound method whereas cls.__call__ returns a bound method


Suppose cls is a class in python. cls.__getattribute__ returns the __getattribute__ function defined on object, but it doesn't bind cls to this function. According to the descriptor protocol, cls is an instance of object and if an attribute is found on the class dictionary, it should return a bound method with cls bound to it.

The same call with cls.__call__ returns a bound method with cls bound to the __call__ function defined on type. Why is there a difference? Also why doesn't cls.__getattribute__ find the attribute in type?

class cls:
    pass

 [In]: cls.__getattribute__
[Out]: slot wrapper '__getattribute__' of 'object' objects>
 [In]: object.__getattribute__
[Out]: <slot wrapper '__getattribute__' of 'object' objects>
 [In]: cls.__getattribute__ is object.__getattribute__
[Out]: True
 [In]: cls.__call__
[Out]: <method-wrapper '__call__' of type object at 0x7fdee07ad110>
 [In]: type.__call__.__get__(cls)
[Out]: <method-wrapper '__call__' of type object at 0x7fdee07ad110>

Solution

  • When you look up cls.__getattribute__, that gets found in cls's own MRO, on cls's superclass object. Like any other method found this way, the lookup returns an unbound method representing the __getattribute__ method of instances of the class.

    On the other hand, when you look up cls.__call__, that doesn't get found in cls's MRO. cls's MRO is (cls, object), and neither cls nor object defines a __call__ method. Instead, this method gets found on cls's own class, its metaclass: it gets found on type. As with any other method found this way, the resulting method is bound to the instance the lookup happened on, that instance being cls itself.

    Thus, cls.__getattribute__ represents the unbound method for getting attributes on instances of cls, while cls.__call__ represents a bound method for calling cls itself.