Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 475
DurgaDatta Avatar asked Jul 14 '16 03:07

DurgaDatta


People also ask

Why not use static methods Python?

Unlike instance methods, static methods aren't bound to an object. In other words, static methods cannot access and modify an object state. In addition, Python doesn't implicitly pass the cls parameter (or the self parameter) to static methods. Therefore, static methods cannot access and modify the class's state.

Is a class callable Python?

Classes are callables Functions are the most obvious callable in Python. Functions can be “called” in every programming language. A class being callable is a bit more unique though. In JavaScript the class instantiation syntax (the way we create an “instance” of a class) involves the new keyword.

What is the difference between @staticmethod and Classmethod?

Class method can access and modify the class state. Static Method cannot access or modify the class state. The class method takes the class as parameter to know about the state of that class. Static methods do not know about class state.

Why is my function not callable?

The “int object is not callable” error occurs when you declare a variable and name it with a built-in function name such as int() , sum() , max() , and others. The error also occurs when you don't specify an arithmetic operator while performing a mathematical operation.


1 Answers

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.

like image 108
matejcik Avatar answered Sep 19 '22 08:09

matejcik