Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Function call and the __call__ attribute

Python's callables are objects that have a __call__ method. They are most of the time functions, but might be class instances as well.

But it so happens that functions do have a __call__ method. Therefore, a __call__ method has a __call__ method as well. The following REPL session shows that we can chain the __call__s:

>>> print
<built-in function print>

>>> print.__call__
<method-wrapper '__call__' of builtin_function_or_method object at 0x0000025E2D597F78>

>>> print.__call__.__call__
<method-wrapper '__call__' of method-wrapper object at 0x0000025E2F631438>

>>> print.__call__.__call__.__call__
<method-wrapper '__call__' of method-wrapper object at 0x0000025E2F5F85F8>

>>> print.__call__.__call__.__call__.__call__
<method-wrapper '__call__' of method-wrapper object at 0x0000025E2F725DA0> 

... and so on. Remarkably, all these methods have different addresses. In addition, they all have the same behaviour:

>>> print("a")
a
>>> print.__call__("a")
a
>>> print.__call__.__call__("a")
a
>>> print.__call__.__call__.__call__("a")

So, when I write print("a"), what is actually called? Is it print, or print.__call__? And what if I define a Foo class with a __call__ method?

Furthermore, how is it possible that every __call__ method has its own different __call__ method? Could it be that they are in fact created when I tried to access them?

like image 684
Right leg Avatar asked Sep 14 '25 10:09

Right leg


1 Answers

Like methods on classes, the __call__ attribute is a descriptor object defined on the type, bound to the object you looked it up on:

>>> type(print).__dict__['__call__']
<slot wrapper '__call__' of 'builtin_function_or_method' objects>
>>> type(print).__dict__['__call__'].__get__(print)
<method-wrapper '__call__' of builtin_function_or_method object at 0x10cc66f78>
>>> print.__call__
<method-wrapper '__call__' of builtin_function_or_method object at 0x10cc66f78>

The binding behaviour (through the __get__ method) is how the resulting method-wrapper instance knows to pass in the print object as self, just like the instance gets passed into methods you defined on a custom Python class.

So yes, that means they are created on demand, and thus will have a unique id. They are otherwise just more instances of the same type.

like image 86
Martijn Pieters Avatar answered Sep 17 '25 01:09

Martijn Pieters