前言

本次依然是选自python面试题系列,将一个比较偏的概念,可能很多人没怎么听说过——猴子补丁,其实所讲的内容很简单,它得益于python灵活的语法、一切皆对象的思想,一起来看看看看吧!

目录

一、什么是monkey patch

二、monkey patch的功能简介

2.1 运行时动态改变类的方法

2.2 monkey patch的应用场景
一、什么是monkey patch

为什么叫猴子补丁?这其实是一个很难回答的问题,似乎和Python语言没啥关系,而且也和它所实现的功能扯不上什么关系,但是偏偏就这么叫了,那就姑且这么称呼吧。

关于猴子补丁的由来网上查到两种说法:

1,这个词原来为Guerrilla Patch,杂牌军、游击队,说明这部分不是原装的,在英文里guerilla发音和gorllia(猩猩)相似,再后来就写了monkey(猴子)。 2,还有一种解释是说由于这种方式将原来的代码弄乱了(messing with it),在英文里叫monkeying about(顽皮的),所以叫做Monkey Patch。

外国人有时候总是会给一些概念莫名其妙的称呼,这就不管他了,关键是理解它的本质和功能才是最重要的。

先明确一个观点:猴子补丁(monkey patch)的主要功能就是动态的属性的替换。虽然属性的运行时替换和猴子也没什么关系,所以说猴子补丁的叫法有些莫名其妙,但是只要和“模块运行时替换的功能”对应就行了。

二、monkey patch的功能简介

monkey patch允许在运行期间动态修改一个类或模块(注意python中一切皆对象,包括类、方法、甚至是模块)

2.1 运行时动态改变类的方法

先看一个简单的例子:

class A:

def func(self):
    print("Hi")
def monkey(self):
    print("Hi, monkey")

a = A()
a.func()
'''运行结果
Hi
'''

上面的结果无可厚非,大家都知道,但是看一下下面的例子:

class A:

def func(self):
    print("Hi")
def monkey(self):
    print("Hi, monkey")

a = A()
A.func=A.monkey #在运行的时候,才改变了func
a.func()
'''运行结果
Hi, monkey
'''

就这么简单,其实这根本的原因在于Python语法的灵活性,方法可以像普通对象那样使用。

我们还可以这样做:

class A:

def func(self):
    print("Hi")
def monkey(self):
    print("Hi, monkey")

def outer_monkey(a): # a 这个参数是没有用到的,因为func有一个参数,如果这个函数没有参数的话不能这样直接赋值

print("Hi,outer monkey")

a = A()
A.func=outer_monkey
a.func()
'''运行结果
Hi, outer monkey
'''

将类外面的普通方法依然可以在程序运行的时候动态赋值给类的某一个方法。

总结:上面所展示的其实就是monkey patch,即运行时动态改变方法、类的方法。其实不管是定义在类外的普通方法、类里面的方法、甚至是模块这些都可以进行“动态替换的操作”,不得不感叹python真的是一门简洁灵活的语言。

2.2 monkey patch的应用场景

这里有一个比较实用的例子,很多代码用到 import json,后来发现ujson性能更高,如果觉得把每个文件的import json 改成 import ujson as json成本较高,或者说想测试一下用ujson替换json是否符合预期,只需要在入口加上:

import json
import ujson

def monkey_patch_json():

json.\_\_name\_\_ \= 'ujson'  
json.dumps \= ujson.dumps  
json.loads \= ujson.loads  

monkey_patch_json()

其实这种场景也比较多,比如我们引用团队通用库里的一个模块,又想丰富模块的功能,除了继承之外也可以考虑用Monkey Patch。个人感觉Monkey Patch带了便利的同时也有搞乱源代码优雅的风险。


陌然浅笑
25 声望3 粉丝

坚持自律