Jan 05, 2020

Python: Replacing or Apply Trigger on a method without touching it

Sometimes, you might need to change a method in a class so that all instances should run your modified method without touching the original method in Python. Let's see how it can be done.

Suppose you have a class like below:


class MyClass:
    def __init__(self, name):
        self.name = name

    def hello_method(self, message):
        print(f'Hello {self.name}, {message}')

When you run following:


obj = MyClass("John")
obj.hello_method("Welcome TechBrij.com!")

Output:

Hello John, Welcome TechBrij.com!

Let's consider a method:


def hi_method(self, message):
    print(f'Hi {self.name}, {message}')

We have to replace hello_method to hi_method


MyClass.hello_method = hi_method
obj = MyClass("John")
obj.hello_method("Welcome TechBrij.com!")

Output:

Hi John, Welcome TechBrij.com!

Suppose instead of replacing, you want to do some operations before and after method call


def wrap_with_method(func):   
    def wrap(*args, **kwargs):
        print('Action before the method call')
        try:
            result = func(*args, **kwargs)        
        except Exception as err:
            print(err)

        print('Action after the method call')        
        return result
    return wrap

        MyClass.hello_method = wrap_with_method(MyClass.hello_method)
        obj = MyClass("John")
        obj.hello_method("Welcome TechBrij.com!")

Output:

Action before the method call
Hello John, Welcome TechBrij.com!
Action after the method call

So you can call any other method before or after the original method call.

Suppose if you run it as multiprocessing pool


	MyClass.hello_method = wrap_with_method(MyClass.hello_method)
        with Pool(processes=1) as obj_pool:
            obj_pool.map_async(obj.hello_method,("Welcome Multiprocessing",))
            obj_pool.close()
            obj_pool.join()

Then you will get error:

AttributeError: 'MyClass' object has no attribute 'wrap'

To fix the error, use functools.wraps like below:


def wrap_with_method(func):   
    @functools.wraps(func)
    def wrap(*args, **kwargs):
        print('Action before the method call')
        try:
            result = func(*args, **kwargs)        
        except Exception as err:
            print(err)

        print('Action after the method call')        
        return result
    return wrap

you need to import functools


import functools

Output:

Action before the method call
Hello John, Welcome Multiprocessing
Action after the method call

So In this post, we saw how to replace method or implement to call another method or action before or after of a method without touching it.

Enjoy Python!!