Methods are functions that are associated with a class. Methods are only created when you retrieve them from an already defined class; a method is a wrapper around a function, with a reference to the class as well (and optionally a reference to the instance).
What happens in the first case is: Python compiles your class definition Adder
. It finds the decorator definition and a function. The decorator is passed the function, returning a new function. That function is added to the class definition (stored in the class __dict__
). All this time you are dealing with a python function, not a method. That happens later.
When you then call a(1)
, a lookup reveals that the instance doesn't have a __call__
but the Adder
class does, so it is retrieved using __getattribute__()
. This finds a function (your deco
decorator), which is a descriptor so it's __get__()
method is called (so Adder.__call__.__get__(a, Adder))
, returning a bound method, which is then called and passed in the 1
value. The method is bound because instance
is not None when __get__()
is called. Your decorator, which wrapped a function at class building time, prints False
because it was passed an unwrapped function to start with.
In the second case, however, you retrieve a method (again via __getattribute__()
calling __get__()
on the undecorated Adder2.__call__
function), this time unbound (as there is no instance, only a class passed to __get__()
(the full call is Adder2.__call__.__get__(None, Adder2)
), and you then decorate that method. Now ismethod()
prints True.
Note that in Python 3, the latter case changes. In Python 3 there no longer is a concept of an unbound method, only functions and bound methods. The term 'bound' is thus dropped altogether. Your second case would also print False
as Adder2.__call__.__get__(None, Adder2)
returns a function in that case.