pythonfunctionclassdecorator

decorate function and method with same decorator


I have a function, a class implementing the similar method and a decorator. The current decorator signature does allow it to be used for the function but doesn't work with the method as expected.

from functools import wraps

def nice(f):
   @wraps(f)
   def decorator(a, b):
       result = f(a, b)
       return 'result is: %s' % str(result)
   
   return decorator

@nice
def sumup(a, b):
   return a + b


class Test:
   def __init__(self):
       pass
   
   @nice
   def sumup(self, a, b):
       return a + b

print(sumup(2, 6))
cls = Test()
print(cls.sumup(4, 8))

Can I somehow 'overload' decorator? In C++ I would be able to write the same decorator again with the same name but different signature and it would work. Can I use some nice solutions in this case? Or I need to just add the same decorator with different name like:

def nice_cls(f):
    @wraps(f)
    def decorator(self, a, b):
        result = f(self, a, b)
        return 'result is: %s' % str(result)
    
    return decorator

and use it for the method?


Solution

  • While I generally agree with @jsbueno 's answer that overloading will require `typing.overload` I'm not sure it is needed for your example.

    Sure, regarding the part of your question about "Can I somehow 'overload' decorator?" yes, you can and you would need `typing.overload` (as already mentioned) and you can read more about it in the official PEP-484 (python-enhancement-proposal) specifically the part about overloading: https://peps.python.org/pep-0484/#function-method-overloading

    However, decorators are going to behave more like a C++ generic constructor for functions in your example through some syntactic sugar. So, regarding the remaining part of your question: "Can I use some nice solutions in this case?" yes, just make the method static.

    The only issue with your example code I see is the class method is not marked as 'static' but implemented as static, yet called on the instance when used.

    e.g, more decorators is simple and works in this case

       # double decorate static class method to drop the unused self argument
       @nice
       @staticmethod
       def sumup(a, b):
           return a + b
    

    This way your @nice decorator takes the "static" "member" method (e.g., instead of the C++ covariant-like argument) output by the @staticmethod decorator which takes the static class function as input,

    OR (as mentioned already) you could also just move the scope of a, b, to the decorator like so:

    def nice(f, *args, **kwargs):
       @wraps(f)
       def decorator(*args, **kwargs):
           result = f(*args, **kwargs)
           return 'result is: %s' % str(result)
       
       return decorator
    

    from my light testing:

    from functools import wraps
    def nice(f, *args, **kwargs):
        @wraps(f)
        def decorator(*args, **kwargs):
            result = f(*args, **kwargs)
            return 'result is: %s' % str(result)
        
        return decorator
    
    
    @nice
    def sumup(a, b):
        return a+ b
    
    
    # >>> sumup(9, 8)
    # 'result is: 17'
    # >>> 
    
    # >>> class Test:
    # ...    def __init__(self):
    # ...        pass
    # ...    
    # ...    @nice
    # ...    def sumup(self, a, b):
    # ...        return a + b
    # ...        
    # >>> cls = Test()
    # ... print(cls.sumup(4, 8))
    # ... 
    # result is: 12
    # >>> 
    

    sorry for the rushed answer, hopefully this helps.