224

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?

Trevor Boyd Smith
  • 18,164
  • 32
  • 127
  • 177
martineau
  • 119,623
  • 25
  • 170
  • 301
  • 5
    If you're asking about Pythonicity, then the standard advice is not to use `staticmethod` at all. They are usually more useful as module-level functions, in which case your problem is not an issue. `classmethod`, on the other hand... – Benjamin Hodgson Oct 03 '12 at 23:22
  • 1
    @poorsod: Yes, I'm aware of that alternative. However in the actual code where I encountered this issue, making the function a static method rather than putting it at module-level makes more sense than it does in the simple example used in my question. – martineau Oct 03 '12 at 23:43
  • FYI in python 3.10 you can call staticmethod functions just fine from the class body. for more info see here: https://stackoverflow.com/a/75628150/52074 – Trevor Boyd Smith Mar 03 '23 at 14:17

7 Answers7

249

update for python version >= 3.10: staticmethod functions can be called from within class scope just fine (for more info see: python issue tracker, or "what's new", or here)


for python version <= 3.9 continue reading

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.

Trevor Boyd Smith
  • 18,164
  • 32
  • 127
  • 177
Ben
  • 68,572
  • 20
  • 126
  • 174
  • 3
    Good update...I was just about to ask how you knew this since I don't see it in the documentation -- which makes me a little nervous about using it because it might be an "implementation detail". – martineau Oct 03 '12 at 23:31
  • After further reading I see that `__func__` is just another name for `im_func` and was added to Py 2.6 for Python 3 forwards-compatibility. – martineau Oct 04 '12 at 00:06
  • 1
    @martineau Not in this context. Bound and unbound method objects have an `im_func` attribute for getting the raw function, and the `__func__` attribute of these method objects is the same as `im_func`. `staticmethod` objects do not have an `im_func` attribute (as shown by my `dir` posting above, and confirmed in an actual interpreter session). – Ben Oct 04 '12 at 00:38
  • 2
    I see, so technically it is undocumented in this context. – martineau Oct 04 '12 at 00:56
  • I'm going to accept your answer now because it still works in Python 2.7.4 and 3.3, so I'm less worried about it being undocumented. Thanks! – martineau Apr 15 '13 at 21:45
  • How do we achieve this when there are more arguments to the function. To me it gives TypeError : function takes exactly 1 argument (0 given) – Akshay Hazari Jan 23 '18 at 05:06
  • 2
    @AkshayHazari The `__func__` attribute of a static method gets you a reference to the original function, exactly as if you had never used the `staticmethod` decorator. So if your function requires arguments, you'll have to pass them when calling `__func__`. The error message you quite sounds like you have not given it any arguments. If the `stat_func` in this post's example took two arguments, you would use `_ANS = stat_func.__func__(arg1, arg2)` – Ben Jan 23 '18 at 05:48
  • @Ben (arg1,arg2) cannot be variables. We would be making a function call. When you call it like that you have to put in literals. I have a different use case. Kind of miss read the question. Got redirected here from somewhere. – Akshay Hazari Jan 23 '18 at 06:24
  • 1
    @AkshayHazari I'm not sure I understand. They can be variables, they just have to be in-scope variables: either defined earlier in the same scope where the class is defined (often global), or defined earlier within the class scope (`stat_func` itself is such a variable). Did you mean you can't use instance attributes of the class you're defining? That's true, but unsurprising; we don't **have** an instance of the class, since we're still defining the class! But anyway, I just meant them as standins for whatever arguments you wanted to pass; you could use literals there. – Ben Jan 23 '18 at 07:41
  • Doesn't seem to work, see https://stackoverflow.com/q/59474374/839733 – Abhijit Sarkar Dec 25 '19 at 00:37
41

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.

Jan Vorcak
  • 19,261
  • 14
  • 54
  • 90
  • While I agree that using `self.__class__.stat_func()` in regular methods has advantages (DRY and all that) over using `Klass.stat_func()`, that wasn't really the topic of my question -- in fact I avoided using the former as not to cloud the issue of the inconsistency. – martineau Dec 10 '14 at 22:06
  • 3
    It's not really an issue of DRY per se. `self.__class__` is better because if a subclass overrides `stat_func`, then `Subclass.method` will call the subclass's `stat_func`. But to be completely honest, in that situation it is much better to just use a real method instead of a static one. – asmeurer Apr 12 '17 at 19:44
  • @asmeurer: I can't use a real method because no instances of the class have been created yet—they can't be since the class itself hasn't even been completely defined yet. – martineau Jun 06 '18 at 19:09
  • I wanted a way to call the static method for my class (which is inherited; so the parent actually has the function) and this seems best by far (and will still work if I override it later). – whitey04 Oct 24 '18 at 20:46
14

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.

Keith
  • 42,110
  • 11
  • 57
  • 76
  • As I said in my question, I understand why the original code didn't work. Can you explain (or provide a link to something that does) why it's considered unPythonic? – martineau Oct 03 '12 at 23:48
  • The staticmethod requires the class object itself to work correctly. But if you call it from within the class at the class level the class isn't really fully defined at that point. So you can't reference it yet. It's ok to call the staticmethod from outside the class, after it's defined. But "[Pythonic](http://faassen.n--tree.net/blog/view/weblog/2005/08/06/0)" is not actually well defined, much like aesthetics. I can tell you my own first impression of that code was not favorable. – Keith Oct 03 '12 at 23:58
  • Impression of which version (or both)? And why, specifically? – martineau Oct 04 '12 at 00:11
  • I can only guess at what your ultimate goal is, but it seems to me that you want a dynamic, class-level attribute. This looks like a job for metaclasses. But yet again there might be a simpler way, or another way to look at the design that eliminates and simplifies without sacrificing the functionality. – Keith Oct 04 '12 at 03:23
  • 5
    No, what I want to do is actually pretty simple -- which is to factor out some common code and reuse it, both to create a private class attribute at class-creation time, and later on within one or more class methods. This common code has no use outside the class, so I naturally want it to be a part of it. Using a metaclass (or a class decorator) would work, but that seems like overkill for something that ought to be easy to do, IMHO. – martineau Oct 04 '12 at 16:32
  • You can create a class attribute at class creation time with this: `_ANS = 42` in the class scope. That is the most common method. – Keith Oct 04 '12 at 16:38
  • 1
    That's fine for class attributes which are trivial constants as in my simplified example code, but not so much if they're not and require some computation. – martineau Oct 04 '12 at 16:50
  • For anyone reading this far, you can use this link, [Pythonic](https://web.archive.org/web/20120904165404id_/http://faassen.n--tree.net/blog/view/weblog/2005/08/06/0), to see what Keith referred to in his first comment (courtesy of [The Wayback Machine](https://web.archive.org/)). – martineau Jun 10 '19 at 19:30
12

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
Pedro Romano
  • 10,973
  • 4
  • 46
  • 50
  • 2
    This is similar to my first attempts at a work around, but I would prefer something that was inside the class...partially because the attribute involved has a leading underscore and is private. – martineau Oct 03 '12 at 23:24
  • Can I first call Klass._ANS = Klass.stat_func() and then define class? Is this not allowed, I see an error class doesn't exist when I try this? – merlachandra Apr 27 '21 at 02:25
10

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
schatten
  • 1,497
  • 1
  • 12
  • 19
  • 2
    +1 for creativity, but I'm no longer worried about using `__func__` because it is now officially documented (see section [_Other Language Changes_](https://docs.python.org/2/whatsnew/2.7.html#other-language-changes) of the What's New in Python 2.7 and its reference to [Issue 5982](http://bugs.python.org/issue5982)). Your solution is even more portable, since it would probably also work in Python versions before to 2.6 (when `__func__` was first introduced as a synonym of `im_func`). – martineau May 16 '14 at 01:12
  • This is the only solution that works with Python 2.6. – benselme Jul 12 '17 at 21:46
  • @benselme: I can't verify your claim because I don't have Python 2.6 installed, but serious doubt it's the only only one... – martineau Jun 06 '18 at 19:12
2

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.

Alechan
  • 817
  • 1
  • 10
  • 24
1

for python version >= 3.10 staticmethod functions can be called from within the class scope just fine

class Tmp:
    @staticmethod
    def func1():
        return 1234
    X = func1() 
print(Tmp.X)
  • my testing shows:
    • python3.9 errors out
    • python3.10 works fine (no errors)
Trevor Boyd Smith
  • 18,164
  • 32
  • 127
  • 177