Home > Back-end >  Do something before and after method execution
Do something before and after method execution

Time:12-05

I was trying to perform an action before and after the execution of some methods in my class.
First I thought about using a decorator to extend the functionality, something like this:

def decorator(f):
    def wrap(*args, **kwargs):
        print("before")
        f(*args, **kwargs)
        print("after")

    return wrap


class Foo(object):

    @decorator
    def do(self):
        print("This is do")


a = Foo()
a.do()

This outputs what I want:

before
This is do
after

But then I realized that if I wanted to inherit from Foo to extend do that wouldn't work, at least the way I'm doing it:

class Bar(Foo):

    def do(self):
        super(Bar, self).do()
        print("This is the new implementation of do")


b = Bar()
b.do()

This will be the output and it's not okay. "after" should be the last thing to be printed:

before
This is do
after
This is the new implementation of do

Maybe there is another way to decorate do in Bar so it does what I want but I don't know how (if there is I would like to know it) and tbh decorating do every time doesn't seem like a good way to go.

So finally I came up with what I think is a nice solution. Defining __getattribute__so it returns a wrapper for do:

class Foo(object):
    def __getattribute__(self, name):
        attribute = super(Foo, self).__getattribute__(name)
        if name == "do":
            def wrap(*args, **kwargs):
                print("before")
                attribute(*args, **kwargs)
                print("after")
            return wrap

        else:
            return attribute

    def do(self):
        print("This is do")


class Bar(Foo):
    def __init__(self):
        super(Bar, self).__init__()

    def do(self):
        super(Bar, self).do()
        print("This is the new implementation of do")


a = Bar()
a.do()

Is this a good solution? Any downsides? I'm missing something that could create a problem in the future? Other workarounds?

Thx!!

CodePudding user response:

You can access the wrapped function in python3 with __wrapped__, since it's a custom decorator you need a slight modification with the functools.wraps decorator.

from functools import wraps

def decorator(f):
    @wraps(f)
    def wrap(*args, **kwargs):
        print("before")
        f(*args, **kwargs)
        print("after")

    return wrap

Reapply the decorator to the new do() and strip it from the old one

class Bar(Foo):
    @decorator
    def do(self):
        super(Bar, self).do.__wrapped__(super)
        print("This is the new implementation of do")

you get:

before
This is do
This is the new implementation of do
after

CodePudding user response:

You could place the implementation of do() in a separate method that will be the one to override in subclasses and have the main do() method with pre/post processing only in the base class:

class Foo:
    def _do(self): 
        # do your stuff

    def do(self):
        self.preProcessing()
        self._do()
        self.postProcessing()

class Bar(Foo):
    def _do(self):
        super()._do()
        # do more stuff 
  • Related