Skip to content Skip to sidebar Skip to footer

Implementing An "after" Decorator On An Abstract Method

I'm trying to write an abstract base class A which will have an abstract method run which the user/developer will be expected to overload. I would like to enforce some 'after' beha

Solution 1:

You actually can not do what you want with a function decorator alone if you do not expect the user to decorate run themselves. You can use class decorators, __init_subclass__, or metaclasses.


With a class decorator,

classA:
    defrun(self):
        passdefdo_the_other_thing(func):
    defwrapper(*args, **kwargs):
        func(*args, **kwargs)
        print('doing the other thing!')
    return wrapper


defpipeline_thing(cls):
    cls.run = do_the_other_thing(cls.run)
    # do some other workreturn cls


@pipeline_thingclassB(A):

    defrun(self):
        print("running!")

Or with __init_subclass__

classA:
    defrun(self):
        passdef__init_subclass__(cls):
        super().__init_subclass__()
        cls.run = do_the_other_thing(cls.run)
        # do some other workclassB(A):

    defrun(self):
         print("running!")

Or with metaclasses

classAMeta(type):

    def__init__(cls, name, bases, attrs, **kwargs):
        super().__init__(name, bases, attrs)
        cls.run = do_the_other_thing(cls.run)
        # do some other workclassA(metaclass=AMeta):
    defrun(self):
        passclassB(A):

    defrun(self):
        print("running!")

This example is overkill for metaclasses (you are using metaclass.__init__ - the least powerful magic method in a metaclass and your behavior can be done with __init_subclass__ (this is the intended use of __init_subclass__). Using a metaclass in this way will prevent your users from using metaclasses and it will unnecessarily complicate your code. If you need the pipeline to do more magic, you can use them (say if you need access to __new__).

I would either use __init_subclass__ or a class decorator (@pipe or something) that also presumably mixed B with A. As, alkasm mentioned, you can make A inherit from abc.ABC and decorate run with abc.abstractmethod to ensure subclasses implement it.

Solution 2:

Don't override run; override a method that runcalls.

classA:
    defrun(self):
        self.do_run()
        print('doing the other thing!')

    defdo_run(self):
        passclassB(A):
    defdo_run(self):
        print('running!') 

Then

>>>B().run() 
running!
doing the other thing!

Post a Comment for "Implementing An "after" Decorator On An Abstract Method"