Calling class staticmethod within the class body?

When I attempt to use a static method from within the body of the class, and define the static method using the built-in staticmethod function as a decorator, like this:

class Klass(object): @staticmethod # use as decorator def _stat_func(): return 42 _ANS = _stat_func() # call the staticmethod def method(self): ret = Klass._stat_func() + Klass._ANS return ret

I get the following error:

Traceback (most recent call last): File "call_staticmethod.py", line 1, in <module> class Klass(object): File "call_staticmethod.py", line 7, in Klass _ANS = _stat_func() TypeError: 'staticmethod' object is not callable

I understand why this is happening (descriptor binding), and can work around it by manually converting _stat_func() into a staticmethod after its last use, like so:

class Klass(object): def _stat_func(): return 42 _ANS = _stat_func() # use the non-staticmethod version _stat_func = staticmethod(_stat_func) # convert function to a static method def method(self): ret = Klass._stat_func() + Klass._ANS return ret

So my question is:

    Are there cleaner or more "Pythonic" ways to accomplish this?

2

6 Answers

staticmethod objects apparently have a __func__ attribute storing the original raw function (makes sense that they had to). So this will work:

class Klass(object): @staticmethod # use as decorator def stat_func(): return 42 _ANS = stat_func.__func__() # call the staticmethod def method(self): ret = Klass.stat_func() return ret

As an aside, though I suspected that a staticmethod object had some sort of attribute storing the original function, I had no idea of the specifics. In the spirit of teaching someone to fish rather than giving them a fish, this is what I did to investigate and find that out (a C&P from my Python session):

>>> class Foo(object):
... @staticmethod
... def foo():
... return 3
... global z
... z = foo
>>> z
<staticmethod object at 0x0000000002E40558>
>>> Foo.foo
<function foo at 0x0000000002E3CBA8>
>>> dir(z)
['__class__', '__delattr__', '__doc__', '__format__', '__func__', '__get__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
>>> z.__func__
<function foo at 0x0000000002E3CBA8>

Similar sorts of digging in an interactive session (dir is very helpful) can often solve these sorts of question very quickly.

10

This is the way I prefer:

class Klass(object): @staticmethod def stat_func(): return 42 _ANS = stat_func.__func__() def method(self): return self.__class__.stat_func() + self.__class__._ANS

I prefer this solution to Klass.stat_func, because of the DRY principle. Reminds me of the reason why there is a new super() in Python 3 :)

But I agree with the others, usually the best choice is to define a module level function.

For instance with @staticmethod function, the recursion might not look very good (You would need to break DRY principle by calling Klass.stat_func inside Klass.stat_func). That's because you don't have reference to self inside static method. With module level function, everything will look OK.

4

What about injecting the class attribute after the class definition?

class Klass(object): @staticmethod # use as decorator def stat_func(): return 42 def method(self): ret = Klass.stat_func() return ret
Klass._ANS = Klass.stat_func() # inject the class attribute with static method value
2

This is due to staticmethod being a descriptor and requires a class-level attribute fetch to exercise the descriptor protocol and get the true callable.

From the source code:

It can be called either on the class (e.g. C.f()) or on an instance (e.g. C().f()); the instance is ignored except for its class.

But not directly from inside the class while it is being defined.

But as one commenter mentioned, this is not really a "Pythonic" design at all. Just use a module level function instead.

8

What about this solution? It does not rely on knowledge of @staticmethod decorator implementation. Inner class StaticMethod plays as a container of static initialization functions.

class Klass(object): class StaticMethod: @staticmethod # use as decorator def _stat_func(): return 42 _ANS = StaticMethod._stat_func() # call the staticmethod def method(self): ret = self.StaticMethod._stat_func() + Klass._ANS return ret
3

If the "core problem" is assigning class variables using functions, an alternative is to use a metaclass (it's kind of "annoying" and "magical" and I agree that the static method should be callable inside the class, but unfortunately it isn't). This way, we can refactor the behavior into a standalone function and don't clutter the class.

class KlassMetaClass(type(object)): @staticmethod def _stat_func(): return 42 def __new__(cls, clsname, bases, attrs): # Call the __new__ method from the Object metaclass super_new = super().__new__(cls, clsname, bases, attrs) # Modify class variable "_ANS" super_new._ANS = cls._stat_func() return super_new
class Klass(object, metaclass=KlassMetaClass): """ Class that will have class variables set pseudo-dynamically by the metaclass """ pass
print(Klass._ANS) # prints 42

Using this alternative "in the real world" may be problematic. I had to use it to override class variables in Django classes, but in other circumstances maybe it's better to go with one of the alternatives from the other answers.

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

You Might Also Like