python notes 69 - what is a monkey patch?

Keywords: Python

preface

What is monkey patch in python?, What are the usage scenarios?
Monkey patch is mainly used for the following purposes:

  • Replace methods, properties, and so on at run time
  • Add previously unsupported functions without modifying third-party codes
  • Add a patch to the object in memory at run time instead of adding it to the source code on disk

Monkey patch

The dynamic replacement of attributes at runtime is called Monkey Patch.
The function is to dynamically replace the module method at run time. Let's start with a simple example
If there is a module somemodule.py, the speak method in this class can be used in other codes

class SomeClass(object):

    def __init__(self):
        self.name = "yoyo"

    def speak(self):
        return "hello world"

Without changing the original code, you can redefine a speck method to replace the original code

from xxx.somemodule import SomeClass


def new_speak(self):
    return "new hello"

# Replace speck method
SomeClass.speak = new_speak

After replacement, the contents of the new method will become content in the new method.

some = SomeClass()
print(some.speak())
# Run result new hello

python custom object to json string

I have defined a class myself, as follows

class MyDefined(object):

    def __init__(self):
        self.name = "yoyo"
        self.age = 18

    def __repr__(self):
        return 'name={}&age={}'.format(self.name, self.age)

When defining the dictionary, the instance of the above class is referenced

aa = {
    "a1": True,
    "b1": "hello",
    "c1": [1, 2, 3],
    "d1": MyDefined()
}
print(aa)
# Running results: {A1 ': true,' B1 ': Hello', 'C1': [1, 2, 3], 'D1': name = yoyo & age = 18}

If the json string is directly converted, an exception will appear: TypeError: Object of type 'MyDefined' is not JSON serializable

import json

print(json.dumps(aa))

Because the MyDefined class is defined by myself, the json library cannot parse into the corresponding string. In this case, you need to write a parsing method yourself

Method 1: you can pass a cls parameter in json.dumps

import json

class MyEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, MyDefined):
            return str(obj)
        else:
            return json.JSONEncoder.default(self, obj)
print(json.dumps(aa, cls=MyEncoder))
# Running result {"A1": true, "B1": "hello", "C1": [1, 2, 3], "D1": "name = yoyo & age = 18"}

Method 2: use monkey patch to solve it

from json import JSONEncoder

def new_default(self, obj):
    if isinstance(obj, MyDefined):
        return str(obj)
    else:
        return json.JSONEncoder.default(self, obj)

JSONEncoder.default = new_default
print(json.dumps(aa))
# Running result {"A1": true, "B1": "hello", "C1": [1, 2, 3], "D1": "name = yoyo & age = 18"}

Replacement module

There is another practical example. Many codes use import json. Later, it was found that ujson has higher performance,
If you think it is expensive to change the import json of each file to import ujson as json, or if you want to test whether it is expected to replace json with ujson, you only need to add:

import json  
import ujson  

def monkey_patch_json():  
    json.__name__ = 'ujson'  
    json.dumps = ujson.dumps  
    json.loads = ujson.loads  

monkey_patch_json()  

It is very useful to replace or add methods during operation. For example, in unit testing, some functions responsible for communicating with external services need to be replaced to facilitate testing.
This technique is not only very common, but also can maintain the maintainability of the code before you finally decide to modify the code. It is a very important skill.
For example, when you install a third-party package in PIP, if you find some bug s in a method or incompatibility with Chinese, do not change the source code at this time (although changing the source code can be solved, it is not conducive to later maintenance. Later, when you change a computer and reinstall pip, you forget where you changed before)
In this case, you can write a method to replace the original method of the third-party package, which is very practical!

Posted by alexboyer on Sun, 21 Nov 2021 22:58:35 -0800