pythondecoratorabstractabstract-methods

write a python decorator inside an abstract class and use it in an inherited class


TL;DR: I want to run some logic (e.g. update database) inside an abstract class. maybe there is another way to do it, that's what i could think of so far (using a decorator)

###################################

I have the following abstract class:

class MyAbstractClass(metaclass=abc.ABCMeta):

    @abc.abstractmethod
    def my_abstract_method(self, request):
        pass

    @staticmethod
    def decorator_func(self, func):
        def run_func(param_one, param_two):
            print('INSIDE run_func', param_one, param_two)
            results = self.update_database(param_one, param_two)
            func(test_id, request)
            print('FINISHED run_func')

        return run_func

    def update_database(self, param_one, param_two):
        results = <DO SOME BACKEND CALCULATIONS>
        return results

I want to create a child class that inherit from this class, and use the decorator_func in order to do some database-updates in the abstract method my_abstract_method:

class ChildClass(MyAbstractClass):
    def __init__(self):
        super().__init__()

    @MyAbstractClass.decorator_func
    def my_abstract_method(self, request):
        get_data()

So i know that my_abstract_method will get updated data because the decorator made sure to run update_database before.

I tried a lot of combinations (e.g. removing the @staticmethod or trying to apply it with @classmethod) but it won't work. for the current code i get the error: run_func() takes 2 positional arguments but 3 were given

Any ideas about how can i easily run update_database before the my_abstract_method will run?


Solution

  • The static method gets one argument, the function being decorated. The function it returns is the one that needs a parameter to hold the implicit instance argument received by a method call.

    @staticmethod
    def decorator_func(func):
        def run_func(self, param_one, param_two):
            print('INSIDE run_func', param_one, param_two)
            results = self.update_database(param_one, param_two)
            func(self, test_id, request)
            print('FINISHED run_func')
    
        return run_func
    

    Because func is a reference to a function, not a bound method, you need to pass self explicitly as the first argument.