Why are descriptors not callable? Basically because they don't need to be. Not every descriptor represents a callable either.
As you correctly note, the descriptor protocol consists of __get__
, __set__
and __del__
. Note no __call__
, that's the technical reason why it's not callable. The actual callable is the return value of your static_method.__get__(...)
.
As for the philosophical reason, let's look at the class. The contents of the __dict__
, or in your case results of vars()
, are basically locals()
of the class
block. If you def
ine a function, it gets dumped as a plain function. If you use a decorator, such as @staticmethod
, it's equivalent to something like:
def _this_is_not_stored_anywhere():
pass
static_method = staticmethod(_this_is_not_stored_anywhere)
I.e., static_method
is assigned a return value of the staticmethod()
function.
Now, function objects actually implement the descriptor protocol - every function has a __get__
method on it. This is where the special self
and the bound-method behavior comes from. See:
def xyz(what):
print(what)
repr(xyz) # '<function xyz at 0x7f8f924bdea0>'
repr(xyz.__get__("hello")) # "<bound method str.xyz of 'hello'>"
xyz.__get__("hello")() # "hello"
Because of how the class calls __get__
, your test.instance_method
binds to the instance and gets it pre-filled as it first argument.
But the whole point of @classmethod
and @staticmethod
is that they do something special to avoid the default "bound method" behavior! So they can't return a plain function. Instead they return a descriptor object with a custom __get__
implementation.
Of course, you could put a __call__
method on this descriptor object, but why? It's code that you don't need in practice; you can almost never touch the descriptor object itself. If you do (in code similar to yours), you still need special handling for descriptors, because a general descriptor doesn't have to be(have like a) callable - properties are descriptors too. So you don't want __call__
in the descriptor protocol. So if a third party "forgets" to implement __call__
on something you consider a "callable", your code will miss it.
Also, the object is a descriptor, not a function. Putting a __call__
method on it would be masking its true nature :) I mean, it's not wrong per se, it's just ... something that you should never need for anything.
BTW, in case of classmethod/staticmethod, you can get back the original function from their __func__
attribute.