Search code examples
pythonobjectpython-2.7instancemonkeypatching

Python monkey patching objects not working as expected


I am trying to learn python monkey patching. I have a simple example where I am trying to monkey patch just a single instance rather than the class itself.

My code:

# add.py
import types


class Math(object):
    def __init__(self):
        self.name = ''

    def add(self, x, y, name):
        self.name = name
        print 'calling from ', self.name
        return x + y

    def monkey_patch(self):
        add = self.add

        def squared_sum(x, y):
            return x**2 + y**2
        add = types.MethodType(squared_sum, self)


if __name__ == '__main__':
    math = Math()
    print math.add(3, 4, 'before monkey_patching')
    math.monkey_patch()
    print math.add(3, 4, 'after monkey_patching')

Expected Output:

calling from  before monkey_patching
7
calling from  after monkey_patching
25

Generated Output:

calling from  before monkey_patching
7
calling from  after monkey_patching
7

Can someone point out where I am going wrong. And also how can I monkey patch the add method when I am doing it from a different file i.e. When I import Math class from add.py in a different file, how can I monkey patch it's add method.


Solution

  • Your code doesn't do what you think it does:

    def monkey_patch(self):
        add = self.add # add now points to self.add
        def squared_sum(x, y):
            return x**2 + y**2
        add = types.MethodType(squared_sum, self) # add now points to squared_sum
    # method ends, add and squared_sum are abandoned
    

    This doesn't actually change self.add. Also, squared_sum doesn't take self or name arguments, unlike add, and doesn't have the print that add does. To make this work fully, do:

    def monkey_patch(self):
        def squared_sum(self, x, y, name):
            self.name = name
            print 'calling from ', self.name
            return x**2 + y**2
        self.add = types.MethodType(squared_sum, self) 
    

    To patch outside the class definition:

    math = Math()
    
    def func(self, x, y, name):
        return x ** y
    
    math.add = types.MethodType(func, math)