Search code examples
pythondecoratorintrospectionpython-decorators

Make a decorator call method-specific pre and post methods in a class


In python 3 I have a base class, from which a class is derived:

class Base:
    # Tell the Base class to look and for pre/post 
    # functions the same name, and call them
    def f(self):
        print("f()")

    def no_prepost(self): # should work, too
        print("nothing")

class Derived(Base):
    def pre_f(self):
        print("pre_f()")

    def post_f(self):
        print("post_f()")

I want to call pre/post methods if they exists, but without explicitly stating them all the time:

foo = Derived()

# if exists: foo.pre_f() -- too verbose, do this automatically!
foo.f()
# if exists: foo.post_f()

Solution

  • A decorator function and some class introspection can do that. If a matching function is found it is called with the same arguments:

    def prepost(f):
        def prepost_wrapper(self, *args, **kwargs):
            pre_name  = 'pre_'  + f.__name__
            post_name = 'post_' + f.__name__
            if hasattr(self, pre_name):  getattr(self, pre_name) (*args, **kwargs)
            ret = f(self, *args, **kwargs)
            if hasattr(self, post_name): getattr(self, post_name)(*args, **kwargs)
            return ret
        return prepost_wrapper
    
    class Base:
        @prepost    
        def f(self, a, b=99):
            print("f()", a, b)
        @prepost
        def missing(self):
            print("nothing special here")
    
    class Derived(Base):
        def pre_f(self, a, b=0): # the arguments must match!
            print("pre_f()", a, b)
        def post_f(self, a, b=1):
            print("post_f()", a, b)
    
    foo = Derived()
    foo.f("abc")
    foo.missing()
    foo.f("xyz", 12)
    

    Output:

    pre_f() abc 0
    f() abc 99
    post_f() abc 1
    nothing special here
    pre_f() xyz 12
    f() xyz 12
    post_f() xyz 12