That's Python doing what it does in order to support dynamically adding functions to classes.
When __get__
is invoked on a function object (usually done via dot access .
on an instance of a class) Python will transform the function to a method and implicitly pass the instance (usually recognized as self
) as the first argument.
In your case, you explicitly call __get__
and explicitly pass the 'instance' 2
which is bound as the first argument of the function x
, here 2
is considered the "instance" self
:
>>> mul2
<bound method mul of 2>
This results in a method bound on the instance 2, with one expected argument that yields the multiplication: calling it returns 2
(the bound argument assigned to x
) multiplied with anything else you supply as the argument y
.
Normally, function()
invokes it's __call__
with the appropriate arguments provided:
mul.__call__(2, 3) # 6
As a plus, a Python implementation of __get__
for functions is provided in the Descriptor HOWTO document of the Python Docs.
Here you can see the transformation, with the usage of types.MethodType
, that takes place when __get__
is invoked :
class Function(object):
. . .
def __get__(self, obj, objtype=None):
"Simulate func_descr_get() in Objects/funcobject.c"
return types.MethodType(self, obj, objtype)
And the source code for the intrigued visitor is located in Objects/funcobject.c
.
As you can see if this descriptor did not exist you'd have to automatically wrap functions in types.MethodType
any time you'd want to dynamically add a function to class which is an unnecessary hassle.