1.4 什么是猴子补丁?

image0

也许你没用过猴子补丁,但是你必须知道,在Python/Ruby 这类脚本语言中,有一种用法叫 Monkey Patch 。

这名字挺好玩的哈。在网上,关于这个名称的来源,大致有如下两种说法:

1、第一种说法。这个用法,原本叫“Guerrilla Patch”,Guerrilla 意为「游击队」,理解起来,就是这个补丁,哪里需要它,它就会出现在哪里,和运行状态打补丁这个功能可以说很形象了。后来老外发现,Guerrilla 和 Gorilla 读音是几乎一样的,说着说着就说成了Gorilla Patch,Gorilla 是大猩猩的意思,有点吓人,那干脆就叫 Monkey Patch 吧。

2、在英文中,monkeying about这个词意为 顽皮的,而猴子补丁这种用法 将原来的代码弄乱了,跟猴子一样调皮,有异曲同工之妙,所以这才将这种用法叫 做猴子补丁。

1.4.1 什么是猴子补丁

在程序运行时给代码加补丁的方法被称为 Monkey Patch 。

用法大概就是如下面这样

from SomeOtherProduct.SomeModule import SomeClass

def speak(self):
    print("ok!")

SomeClass.speak = speak

它从代码中可以看出,它可以在需要的时候(运行时),给外部模块(内建,第三方模块,自定义模块) 添加/替换 方法。

  • 在运行时替换方法、属性等

  • 在不修改第三方代码的情况下增加原来不支持的功能

  • 在运行时为内存中的对象增加patch而不是在磁盘的源代码中增加

1.4.2 实例讲解

这里举一个添加方法的例子

import pandas as pd
def just_foo_cols(self):
    """Get a list of column names containing the string 'foo'

    """
    return [x for x in self.columns if 'foo' in x]

pd.DataFrame.just_foo_cols = just_foo_cols # monkey-patch the DataFrame class
df = pd.DataFrame([list(range(4))], columns=["A","foo","foozball","bar"])
df.just_foo_cols()
del pd.DataFrame.just_foo_cols # you can also remove the new method

在openstack中的例子

image1

还有就是gevent中也有用到。

1.4.3 如何使用猴子补丁?

为实例绑定对象(扩展__slot__)

import types
import logging


def get_logger():
    logger = logging.getLogger(__name__)
    logger.setLevel(logging.INFO)
    handler = logging.FileHandler('monkey_patch.log')
    handler.setLevel(logging.INFO)

    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    handler.setFormatter(formatter)

    logger.addHandler(handler)
    return logger


LOG = get_logger()


def info(self, msg, *args, **kwargs):
    print(msg)

    INFO = 20
    if self.isEnabledFor(INFO):
        self._log(INFO, msg, args, **kwargs)

# 重点
LOG.info = types.MethodType(info, LOG)

LOG.info('hello, world')

给函数添加装饰器

func = importutils.import_class("%s.%s" % (module, key))
setattr(sys.modules[module], key,
        decorator("%s.%s" % (module, key), func))

image2