Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
974 views
in Technique[技术] by (71.8m points)

oop - Why are python static/class method not callable?

Why are python instance methods callable, but static methods and class methods not callable?

I did the following:

class Test():
    class_var = 42

    @classmethod 
    def class_method(cls):
        pass 

    @staticmethod
    def static_method():
        pass

    def instance_method(self):
        pass 

for attr, val in vars(Test).items():
    if not attr.startswith("__"):
        print (attr, "is %s callable" % ("" if callable(val) else "NOT"))

The result is:

static_method is NOT callable
instance_method is  callable
class_method is NOT callable
class_var is NOT callable

Technically this may be because instance method object might have a particular attribute (not) set in a particular way (possibly __call__). Why such asymmetry, or what purpose does it serve?

I came across this while learning python inspection tools.

Additional remarks from comments:

The SO answer linked in the comments says that the static/class methods are descriptors , which are not callable. Now I am curious, why are descriptors made not callable, since descriptors are class with particular attributes (one of __get__, __set__, __del___) defined.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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 define 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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...