1、猴子补丁就是不改变原有模块的内容的前提下,给原有模块新增方法或者修改原有模块。

一个模块的函数,如果希望改变函数的功能,不改变函数名,通常是库模块,你不可能去修改三方库的源码的,实施起来不方便,而且假设你直接在原处改了东西,别人继续用那个库模块,你没和别人提前打好招呼,那可能产生悲剧。

2、面向对象中,替换一个方法,继承重写方法就可以,当然也可以直接给原类的方法重新赋值一个函数对象,这也算猴子补丁。

面向过程是模块加函数的写法,不能继承。

此时做法可以是猴子补丁,就是重新给模块的某个函数赋值为一个自定义的函数对象。

3、以json为例,json只能解析基本的字符串  数字啥的,对大多数三方类型都不支持。

例如最常见的是从数据库中读取的时间字段,得到结果时候是datetime对象,此时直接json.dumps会造成解析错误。

但我原来谢了很多代码,之前的数据库没有时间类型的字段,所以现在有了时间后,原来的代码大面积出错,报TypeError: datetime.datetime(2018, 7, 12, 19, 44, 19, 141200) is not JSON serializable这个错误。

为了不一一修改,那就用monkey技术,

以下文件是我的utils包的__init__.py文件

这样做了后,

1)原来的所有其他地方的json.dumps代码不用做任何修改,就可以直接解析时间了,

2)json.dumps方法解析有中文的字典时候很蛋疼,必须设置ensure_ascii=False才能显示汉字,每次都加这几个字也很麻烦。现在用了monkey patch,原来所有没设置这个参数的json.dumps方法现在也能直接解析出中文了。

3)由于我所有代码几乎都import了utils包,直接在包里面执行了monkey_patch_json()方法,所以不用再去单独模块调用pacth函数了。



import json
from .log_manager import LogManager, get_logs_dir_by_folder_name
from .redis_manager import RedisManagerfrom . import decorators, config_ydf
from .currency_converter import CurrencyConverter
from .local_ip_query import get_host_ip
from datetime import datetime as _datetime
from datetime import date as _date


def show_sys_path():
    import sys
    print(sys.path)


class _CustomEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, _datetime):
            return obj.strftime('%Y-%m-%d %H:%M:%S')
        elif isinstance(obj, _date):
            return obj.strftime('%Y-%m-%d')
        else:
            return json.JSONEncoder.default(self, obj)


def _dumps(obj, skipkeys=False, ensure_ascii=False, check_circular=True,
          allow_nan=True, cls=_CustomEncoder, indent=None, separators=None,
          default=None, sort_keys=False, **kw):
    if (not skipkeys and ensure_ascii and
            check_circular and allow_nan and
            cls is None and indent is None and separators is None and
            default is None and not sort_keys and not kw):
        return json._default_encoder.encode(obj)  # noqa
    return cls(
        skipkeys=skipkeys, ensure_ascii=ensure_ascii,
        check_circular=check_circular, allow_nan=allow_nan, indent=indent,
        separators=separators, default=default, sort_keys=sort_keys, ).encode(obj)


def monkey_patch_json():
    json.dumps = _dumps


monkey_patch_json()  # pacth掉json模块的dumps方法



monkey不光可以patch三方库,其他方面也包括,比如原来写了个A类,现在想要试用B类的效果,只要你保证A和B的公有方法和属性的名字都是一样的,然后直接写A = B就可以了,

假如A类在其他各个文件中被使用了几百次,那么你不需要去每个地方修改一下,因为调用A,但实际使用的是B。