Python decorator as a staticmethod

前端 未结 4 1703
天涯浪人
天涯浪人 2020-11-28 10:42

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 d

相关标签:
4条回答
  • 2020-11-28 10:46

    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):
        @staticmethod
        def f():
            pass
    print A.f
    print A.__dict__["f"]
    

    prints

    <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.

    0 讨论(0)
  • 2020-11-28 10:46

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

    You can return a non-static method to a static_method

    http://docs.python.org/library/functions.html#staticmethod

    0 讨论(0)
  • 2020-11-28 10:54

    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
    
        @staticmethod
        def _returnBaseAndResult(func):
            from functools import wraps
            @wraps(func)
            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()
        print(a.square())
    

    Will print (2, 4)

    0 讨论(0)
  • 2020-11-28 10:57

    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).

    So,

    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
    
        @black.ensure_same_color
        def get():
            return 'Here is your shiny new T-Ford'
    
    0 讨论(0)
提交回复
热议问题