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!