Reputation: 1685
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?
Upvotes: 133
Views: 85692
Reputation: 121
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
Upvotes: 12
Reputation: 13759
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
Upvotes: 2
Reputation: 21
class A(object):
def open():
return 123
def proccess():
return 456
switch = {
1: lambda : A.open(),
2: lambda : A.proccess(),
}
Upvotes: 2
Reputation: 1087
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
.
A.open()
and A.process()
, just like common static methods.open()
and process()
, without A.
. However,
A.open
will fail. (I tested this case only with the decorator use. (@open
))Upvotes: 8
Reputation: 1124758
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.
Upvotes: 162