I stumbled upon this code that I found weird as it seems to violate the fact that python builtins call dunder methods directly from the class of the object. Using __call__ as an example, if we define class A as following:
class A:
@property
def __call__(self):
def inner():
return 'Called.'
return inner
a = A()
a() # return 'Called.'
type(a).__call__(a) # return 'property' object is not callable.
However, this behaviour seems to contradict with what's said in Python's official documentation:
object.__call__(self[, args...])Called when the instance is “called” as a function; if this method is defined,x(arg1, arg2, ...)roughly translates totype(x).__call__(x, arg1, ...).
Can anyone explain what is going on here?
Yes, but it respects the way to retrieve a method from a given function - We can see that the
__get__method is called:On the code bellow, I just replaced
propertywith a simpler descriptor that will retrieve its "func" - and used it as the__call__method.So, the "dunder" functionality just retrieved the method through its
__get__as usual for all methods. What was skipped is the step that goes through__getattribute__- Python will go directly to the__call__slot in theAclass, and not go through the normal lookup sequence, by calling__getattribute__, which starts at the class (for a descriptor), then looks at the instance, than back to the class (for a regular attribute): it assumes that if there is something at the instance's class dunder slot it is a method, and uses its__get__method accordingly.A function's
__get__is used when retrieving it from an instance - as that is the mechanism that injects the instance as theselfargument for the call.And
__get__is exactly the thing thatpropertyreplaces to perform its "magic".To demonstrate that
__getatribute__is not called, in iPython, I had to encapsulate "y" inside a dummy function, otherwise iPython would trigger__getattribute__when trying to autocomplete stuff: