首页 > 解决方案 > In Python, how do I change/insert code of a method by other objects/functions/methods

问题描述

I am not new to python but I am far from being an expert (or intermediate). Right now, I play around with objects and their behavior (like setattr, monkey-patch, etc.). During this, I stumbled upon a problem where I do not have any idea on how this might work.

Imagine following code:

class MyCalculator():
    def __init__(self):
        pass

    def addition(self, a, b):
        return a + b

    def substraction(self, a, b):
        return a - b

import inspect

class Changing():
    def __init__(self):
        pass

    def listUserMethods(self, myObject):
        object_names = [object_name for object_name in inspect.getmembers(myObject) if (inspect.ismethod(object_name[1]))]
        return object_names

    def setMethodAttribute(self, myMethod):
        pass


if __name__=="__main__":
    myCalc = MyCalculator()
    change = Changing()

Now, I would like that setMethodAttribute() will change the code of the method I provide itself. Like, inserting a print() statement before the rest of the original method is executed. E.g. printing the input parameter before executing the addition, etc. In my case, this does not need to be done during runtime (even if this is very interesting to know). I could imagine, that using inheritance or something similar could be a way. Perhaps somebody has a great idea?

Thanks for the help!

标签: pythonmethods

解决方案


The answer really depends what you are after.

Wrapping a method of a class (before runtime)

This is very typical use case of decorators (the @something above a function definition).

def with_printing(func):
    def wrapper(*args, **kwargs):
        print("Before calling method")
        ret = func(*args, **kwargs)
        print("After calling method")
        return ret

    return wrapper


class MyCalculator:
    @with_printing
    def addition(self, a, b):
        print("calling addition")
        return a + b

If you want to keep the docstring of the original method, you would use the functools.wraps().

Example output

mycalc = MyCalculator()
print(mycalc.addition(2, 3))

would print

Before calling method
calling addition
After calling method
5

Wrapping a method of an object instance (runtime)

Here is one implementation which changes the method of an object. Note that this changes the method of an instance and not every instance of that class.

class MyCalculator:
    def addition(self, a, b):
        print("calling addition")
        return a + b


class Changing:
    def set_method_attribute(self, obj, method_name):

        method = getattr(obj, method_name)

        def wrapper(*args, **kwargs):
            print("Before calling method")
            ret = method(*args, **kwargs)
            print("After calling method")
            return ret

        setattr(obj, method_name, wrapper)

Example usage

# Create two calculator objects for testing
mycalc = MyCalculator()
mycalc2 = MyCalculator()

change = Changing()

# Before change
print(mycalc.addition(2, 3))
print("----")

# After change
change.set_method_attribute(mycalc, "addition")
print(mycalc.addition(2, 3))
print("----")

# The another calculator object remains unchanged
print(mycalc2.addition(2, 3))

will print

calling addition
5
----
Before calling method
calling addition
After calling method
5
----
calling addition
5

推荐阅读