猴子补丁
-
在运行时替换方法、属性等,动态属性替换
-
在不修改第三方代码的情况下增加原来不支持的功能
-
在运行时为内存中的对象增加patch而不是在磁盘的源代码中增加
-
主要用途在于源码不宜直接修改,而添加补充功能
-
例子:python自带的json包不支持自定义对象转json串,在python中用json.dumps转自定义对象时会报异常class is not JSON serializable,通过增加一段代码补丁(称作猴子补丁)便可实现自定义转换,补丁代码如下:
1 import re 2 old_match = re.match # 保留旧函数,后面可以直接用 3 4 def new_match(*args, **kwargs): 5 print('re.match is running') # 增加自己的逻辑 6 return old_match(*args, **kwargs) # 调用回旧函数 7 8 re.match = new_match # 使用新的函数地址替换旧的函数地址 9 10 # 一个全新的函数 11 def hello(*args, **kwargs): 12 print(args, kwargs) 13 re.hello = hello # 赋予一个新属性(函数) 14 15 """ 16 >>> re.match('.*', 'abc') 17 re.match is running # 如果没有 patch,此行不会输出 18 <re.Match object; span=(0, 3), match='abc'> 19 >>> re.hello(123) # 如果没有 patch,调用这个函数会报错 20 (123,) {} 21 """
1 from json import JSONEncoder 2 """ 3 自定义_default补丁方法,传入对象 4 先判断传入的对象中有没有to_json属性,如果没有,则走对象默认的方法,若有则走to_json 5 """ 6 def _default(self, obj): 7 return getattr(obj.__class__, "to_json", JSONEncoder().default)(obj) 8 JSONEncoder.default = _default 9 10 class Tmp: 11 def __init__(self, id, name): 12 self.id = id 13 self.name = name 14 15 def to_json(): 16 # 返回自定义对象json串 17 pass
通过补丁代码我们可以看到,代码替换了json包的默认转json的方法,运行了补丁代码后,转json的过程变成了先找对象的to_json属性,在没有to_json属性的情况下才使用默认的JSONEncoder.default的方法,也就是通过这么一个patch,增加了json包原来没有的功能。同时也不影响其他调用者使用JSONEncoder.default的方法(推荐这么写,降低改变源代码的风险)
猴子补丁与装饰器的区别
作用时期不同:猴子补丁作用于运行时,装饰器作用于定义时
由于猴子补丁是直接用新的函数地址覆盖源代码的函数地址,所以需要慎用