134

I have this code:

class A(object): @staticmethod def open(): return 123 @staticmethod def proccess(): return 456 switch = { 1: open, 2: proccess, } obj = A.switch[1]() 

When I run this I keep getting the error:

TypeError: 'staticmethod' object is not callable 

how to resolve it?

6
  • stackoverflow.com/questions/3798835/… Commented Jan 29, 2017 at 13:13
  • Possible duplicate of 'classmethod' object is not callable Commented Jan 29, 2017 at 13:13
  • @melpomene: oops, I'm not sure why I missed that. Commented Jan 29, 2017 at 13:20
  • @melpomene: not entirely a dupe, you have different (more) options with staticmethod objects. Commented Jan 29, 2017 at 13:21
  • @MartijnPieters Oh, I didn't quite realize that the question title doesn't match the error message in the body. Commented Jan 29, 2017 at 13:23

5 Answers 5

162

You are storing unbound staticmethod objects in a dictionary. Such objects (as well as classmethod objects, functions and property objects) are only bound through the descriptor protocol, by accessing the name as an attribute on the class or an instance. Directly accessing the staticmethod objects in the class body is not an attribute access.

Either create the dictionary after creating the class (so you access them as attributes), or bind explicitly, or extract the original function before storing them in the dictionary.

Note that 'binding' for staticmethod objects merely means that the context is merely ignored; a bound staticmethod returns the underlying function unchanged.

So your options are to unindent the dictionary and trigger the descriptor protocol by using attributes:

class A(object): @staticmethod def open(): return 123 @staticmethod def proccess(): return 456 A.switch = { 1: A.open, 2: A.proccess, } 

or to bind explicitly, passing in a dummy context (which will be ignored anyway):

class A(object): @staticmethod def open(): return 123 @staticmethod def proccess(): return 456 switch = { 1: open.__get__(object), 2: proccess.__get__(object), } 

or access the underlying function directly with the __func__ attribute:

class A(object): @staticmethod def open(): return 123 @staticmethod def proccess(): return 456 switch = { 1: open.__func__, 2: proccess.__func__, } 

However, if all you are trying to do is provide a namespace for a bunch of functions, then you should not use a class object in the first place. Put the functions in a module. That way you don't have to use staticmethod decorators in the first place and don't have to unwrap them again.

Sign up to request clarification or add additional context in comments.

3 Comments

The note at the end is the most important part, IMO. We can just create a list of all the functions we want from a module, and call them in a loop.
Both open.__func__ and open.__get__(object) get PyCharm warnings, though work. open.__get__(object, None) gets no warnings and works too
@SergeyNudnov: I suspect PyCharm would be happier about open.__func__ if you used a type guard like inspect.ismethod() on it first. That PyCharm complains about open.__get__(object) sounds like a bug, the __get__ descriptor protocol method is is documented to take a default value for the owner argument.
12

Note that starting with Python 3.10 the workarounds given in the accepted answer are no longer needed (but still work):

Moreover, static methods are now callable as regular functions. (Contributed by Victor Stinner in bpo-43682.)

For more details see: https://docs.python.org/3/whatsnew/3.10.html

Python 3.10.2 Type "help", "copyright", "credits" or "license" for more information. >>> class A(): ... @staticmethod ... def open(): ... return 123 ... @staticmethod ... def process(): ... return 456 ... switch = { ... 1: open, ... 2: process ... } ... >>> A.switch[1]() 123 

Comments

8

In addition to Pieters' answer, you can just drop @staticmethod:

class A(object): def open(): return 123 def proccess(): return 456 switch = { 1: open, 2: proccess, } obj = A.switch[1]() 

However, in this way it becomes impossible to call open and process with self.

  • Outside the class they can be called either with A.open() and A.process(), just like common static methods.
  • Inside the class they can be called with just open() and process(), without A.. However,
    • A.open will fail. (I tested this case only with the decorator use. (@open))
    • What's more, they must be placed before the function calling them.

2 Comments

With this being possible why is @staticmethod decorator even included in python?
@Rhdr I think @staticmethod makes it possible to access static methods with self., self.__dict__ and self.__getattribute__, consistent with common dynamic methods. (I haven't tried __dict__/__getattribute__, but they should work) (See the implementation of @staticmethod at stackoverflow.com/a/60383279/8535456)
2
class A(object): def open(): return 123 def proccess(): return 456 switch = { 1: lambda : A.open(), 2: lambda : A.proccess(), } 

2 Comments

Adding an explanation or a short note for how/what your code fixes the issue is a good idea!
Interesting approach, but if there are any parameters to the methods, there will be much more code there
2

For Unit Tests

Thanks to and based on Martijn answer, both __func__ and __get__(TestCase) options work well when using static methods on tests, example:

from unittest import TestCase, mock from unittest.mock import MagicMock, Mock class TestMother(TestCase): @staticmethod def side_effect_me(param: str) -> str: return param.capitalize() @mock.patch("src.some_folder.MyModule.my_func", \ new=MagicMock(side_effect=side_effect_me.__func__)) def test_should_cap(self): # test stuff 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.