Python decorator as a staticmethod

Posted on

Question :

Python decorator as a staticmethod

I’m trying to write a python class which uses a decorator function that needs information of the instance state. This is working as intended, but if I explicitly make the decorator a staticmetod, I get the following error:

Traceback (most recent call last):
  File "", line 1, in <module>
    class TFord(object):
  File "", line 14, in TFord
TypeError: 'staticmethod' object is not callable


Here is the code:

class TFord(object):
    def __init__(self, color):
        self.color = color

    def ensure_black(func):
        def _aux(self, *args, **kwargs):
            if self.color == 'black':
                return func(*args, **kwargs)
                return None
        return _aux

    def get():
        return 'Here is your shiny new T-Ford'

if __name__ == '__main__':
    ford_red = TFord('red')
    ford_black = TFord('black')

    print ford_red.get()
    print ford_black.get()

And if I just remove the line @staticmethod, everything works, but I do not understand why. Shouldn’t it need self as a first argument?

Asked By: wkz


Answer #1:

This is not how staticmethod is supposed to be used. staticmethod objects are descriptors that return the wrapped object, so they only work when accessed as classname.staticmethodname. Example

class A(object):
    def f():
print A.f
print A.__dict__["f"]


<function f at 0x8af45dc>
<staticmethod object at 0x8aa6a94>

Inside the scope of A, you would always get the latter object, which is not callable.

I’d strongly recommend to move the decorator to the module scope — it does not seem to belong inside the class. If you want to keep it inside the class, don’t make it a staticmethod, but rather simply del it at the end of the class body — it’s not meant to be used from outside the class in this case.

Answered By: Sven Marnach

Answer #2:

Python classes are created at runtime, after evaluating the contents of the class declaration. The class is evaluated by assigned all declared variables and functions to a special dictionary and using that dictionary to call type.__new__ (see customizing class creation).


class A(B):
    c = 1

is equivalent to:

A = type.__new__("A", (B,), {"c": 1})

When you annotate a method with @staticmethod, there is some special magic that happens AFTER the class is created with type.__new__. Inside class declaration scope, the @staticmethod function is just an instance of a staticmethod object, which you can’t call. The decorator probably should just be declared above the class definition in the same module OR in a separate “decorate” module (depends on how many decorators you have). In general decorators should be declared outside of a class. One notable exception is the property class (see properties). In your case having the decorator inside a class declaration might make sense if you had something like a color class:

class Color(object):

    def ___init__(self, color):
        self.color = color

     def ensure_same_color(f):

black = Color("black")

class TFord(object):
    def __init__(self, color):
        self.color = color

    def get():
        return 'Here is your shiny new T-Ford'
Answered By: jfocht

Answer #3:

Solution does exist!

Problem is that Static method that is trying to be used as decorator is in fact staticmethod object and is not callable.

Solution: staticmethod object has method __get__ which takes any argument and returns real method: python documentation Python 3.5 and up:

class StaticMethod(object):
    "Emulate PyStaticMethod_Type() in Objects/funcobject.c"

    def __init__(self, f):
        self.f = f

    def __get__(self, obj, objtype=None):
        return self.f

Min solution I came with is:

class A():
    def __init__(self):
        self.n =  2

    def _returnBaseAndResult(func):
        from functools import wraps
        def wrapper(*args, **kwargs):
            self = args[0]
            response = func(*args, **kwargs)
            return self.n, response
        return wrapper

    @_returnBaseAndResult.__get__('this can be anything')
    def square(self):
        return self.n**2

if __name__ == '__main__':
    a = A()

Will print (2, 4)

Answered By: usernumber124153

Answer #4:

ensure_black is returning a _aux method that isn’t decorated by @staticmethod

You can return a non-static method to a static_method

Answered By: Felipe Cruz

Leave a Reply

Your email address will not be published.