為什么叫猴子補(bǔ)丁?
猴子補(bǔ)丁的這個叫法起源于Zope框架,大家在修正Zope的Bug的時(shí)候經(jīng)常在程序后面追加更新部分,這些被稱作是“雜牌軍補(bǔ)丁(guerillapatch)”,后來guerilla就漸漸的寫成了gorllia(猩猩),再后來就寫了monkey(猴子),所以猴子補(bǔ)丁的叫法是這么莫名其妙的得來的。
還有種說法是由于這種方式將原來的代碼弄亂了(messing with it),在英文里叫monkeying about(頑皮的),所以叫做Monkey Patch(猴子補(bǔ)丁)。
程序中對猴子補(bǔ)丁的解釋
猴子補(bǔ)丁指在運(yùn)行時(shí)動態(tài)改變類或模塊,為的是將第三方代碼打補(bǔ)丁在不按預(yù)期運(yùn)行的bug或者feature上 。
在運(yùn)行時(shí)動態(tài)修改模塊、類或函數(shù),通常是添加功能或修正缺陷。猴子補(bǔ)丁在代碼運(yùn)行時(shí)內(nèi)存中發(fā)揮作用,不會修改源碼,因此只對當(dāng)前運(yùn)行的程序?qū)嵗行А?/p>
因?yàn)楹镒友a(bǔ)丁破壞了封裝,而且容易導(dǎo)致程序與補(bǔ)丁代碼的實(shí)現(xiàn)細(xì)節(jié)緊密耦合,所以被視為臨時(shí)的變通方案,不是集成代碼的推薦方式。
代碼解釋
我假裝有一個翻譯員的模塊,它能使用各種語言print出“你好”,
translator.py
class Translator:
def __init__(self):
pass
def chinese(self):
print("你好!")
def japanese(self):
print("空你兄哇!")
def english(self):
print("Hello!")
然后在另外一個文件調(diào)用這個模塊,
demo.py
from translator import Translator
t = Translator()
t.japanese()
t.english()
t.chinese()
執(zhí)行結(jié)果:
空你兄哇!
Hello!
你好!
然后一個廣東的程序員用這個模塊的時(shí)候,希望調(diào)用chinese的時(shí)候能print“雷猴”而不是“你好”。
再不改變原來模塊(庫)的情況下,我們可以這樣給這個模塊(庫)打上我們需要的補(bǔ)丁。demo.py
from translator import Translator
t = Translator()
t.japanese()
t.english()
t.chinese()
def canton(self):
print("雷猴!")
Translator.chinese = canton
t2 = Translator()
t2.chinese()
執(zhí)行結(jié)果:
空你兄哇!
Hello!
你好!
雷猴!
猴子補(bǔ)丁的實(shí)際應(yīng)用場景
stackoverflow上有個比較熱的猴子補(bǔ)丁使用案例,很多代碼用到 import json,后來發(fā)現(xiàn)ujson性能更高,如果覺得把每個文件的import json 改成 import ujson as json成本較高,或者說想測試一下用ujson替換json是否符合預(yù)期,只需要在入口加上:
import json
import ujson
def monkey_patch_json():
json.__name__ = 'ujson'
json.dumps = ujson.dumps
json.loads = ujson.loads
monkey_patch_json()
猴子補(bǔ)丁的功能很強(qiáng)大,但是也帶來了很多的風(fēng)險(xiǎn),以上對json模塊的修改可能會使整個Python/ target=_blank class=infotextkey>Python進(jìn)程所使用的json模塊都會被替換。
可能自己的代碼能hold住,但是其它第三方庫,有時(shí)候問題并不好排查,即使排查出來也是很棘手。