devnext
devnext

Reputation: 902

How i can to inject code in class method in python?

How i can to inject code in class method.i have to use this many method (app_one app_two...). i think code is not beauty and look like to duplicate in multiline. i want to know how i can to refactor this code ?

class Main:
     def step_one(self):
         print("STEP 1")

     def step_tree(self):
         print("STEP 3")
     
     def app_one(self):
         self.step_one()
         /// do_something
         self.step_two()

     def app_two(self):
         self.step_one()
         /// do_something
         self.step_two()

     def app_three(self):
         self.step_one()
         /// do_something
         self.step_two()

thank for expert.

Upvotes: 3

Views: 906

Answers (3)

Colin Wang
Colin Wang

Reputation: 23

something like this?

class Main:
     def step_one(self):
         print("STEP 1")

     def step_tree(self):
         print("STEP 3")
     
     def app_one_function(self):
         # do_something_app_one_unique

     def app_two_function(self):
         # do_something_app_two_unique

     def app_three_function(self):
         # do_something_app_three_unique

     def run_app(self, function):
         self.step_one()
         function()
         self.step_two()

Or you could use decorator like this

def decorator(function):
    def decorated(cls):
        cls.step_one()
        function(cls)
        cls.step_two()
    return decorated

class Main:
     def step_one(self):
         print("STEP 1")

     def step_tree(self):
         print("STEP 3")
     
     @decorator
     def app_one(self):
         # do_something_app_one_unique

     @decorator
     def app_two(self):
         # do_something_app_two_unique

     @decorator
     def app_three(self):
         # do_something_app_three_unique

obj = Main()
obj.app_one()
obj.app_two()

Upvotes: 1

Silvio Mayolo
Silvio Mayolo

Reputation: 70287

We can define a decorator that takes an ordinary instance method and turns it into one with your desired prologue and epilogue. Here's what our decorator will look like

def wrapped_app(fn):
    def wrapped_fn(self, *args, **kwargs):
        self.step_one()
        fn(self, *args, **kwargs)
        self.step_three()
    return wrapped_fn

So given a function fn, wrapped_app returns a new function (which we locally call wrapped_fn) which does self.step_one(), then fn, then self.step_three(). We can use it with decorator syntax inside the class now

class Main:

    def step_one(self):
        print("STEP 1")

    def step_three(self):
        print("STEP 3")

    @wrapped_app
    def app_one(self):
        print("App one")

    @wrapped_app
    def app_two(self):
        print("App two")

    @wrapped_app
    def app_three(self):
        print("App three")

We can check that it works as follows

>>> main = Main()
>>> main.app_one()
STEP 1
App one
STEP 3

We can also go one step further. Your current app_one et al don't return anything, but it is possible that we may want such functions to be capable of returning. A slight modification to wrapped_fn will take care of that eventuality. Similarly, we may also want step_three to be guaranteed to run, even in the case that our app function fails or raises an exception. We can use try ... finally to do so.

def wrapped_app(fn):
    def wrapped_fn(self, *args, **kwargs):
        self.step_one()
        try:
            result = fn(self, *args, **kwargs)
        finally:
            self.step_three()
        return result
    return wrapped_fn

Upvotes: 4

Brian Destura
Brian Destura

Reputation: 12068

One approach is to use decorators:

from functools import wraps

def call_step_one_step_two(func):
    @wraps(func)
    def wrapper(self, *args, **kwargs):
        self.step_one()
        func(self, *args, **kwargs)
        self.step_two()
    return wrapper


class Main:
     def step_one(self):
        print("STEP 1")

     def step_two(self):
        print("STEP 3")

     @call_step_one_step_two
     def app_one(self):
        // do something

     @call_step_one_step_two
     def app_two(self):
        // do something

     @call_step_one_step_two
     def app_three(self):
        // do something

All methods wrapped in this decorator will always call step_one before the method, and step_two after the method. This does not support returning values though. Hope this is enough.

Upvotes: 2

Related Questions