中易网

什么是猴子补丁

答案:1  悬赏:70  
解决时间 2021-02-16 05:44
什么是猴子补丁
最佳答案
你好,很高兴为你解答。
猴子补丁的由来
首先说个我自己的笑话,话说Python算是我接触的稍微深点儿的第一门动态语言,用Python没多久就知道了有个Gevent,学习Gevent没多久就知道有个“猴子补丁”的概念。最开始觉得这么名字挺乐呵,猴子补丁,为啥叫这么个名儿?是因为猴子的动作迅速灵敏,Gevent也有这个特点,所以叫猴子补丁么?

然后这几天在看《松本行弘的程序世界》这本书,里面专门有一章讲了猴子补丁的设计,我就笑了,原来猴子补丁不是我理解的这个意思,更不是Gevent最开始这么做的。所谓的猴子补丁的含义是指在动态语言中,不去改变源码而对功能进行追加和变更。猴子补丁的这个叫法起源于Zope框架,大家在修正Zope的Bug的时候经常在程序后面追加更新部分,这些被称作是“杂牌军补丁(guerilla patch)”,后来guerilla就渐渐的写成了gorllia(猩猩),再后来就写了monkey(猴子),所以猴子补丁的叫法是这么莫名其妙的得来的。

从Gevent学习猴子补丁的设计
猴子补丁这种东西充分利用了动态语言的灵活性,可以对现有的语言Api进行追加,替换,修改Bug,甚至性能优化等等。比如gevent的猴子补丁就可以对ssl、socket、os、time、select、thread、subprocess、sys等模块的功能进行了增强和替换。我们来看下gevent中的猴子补丁模块gevent.monkey的设计和实现,以后如果自己要设计实现猴子补丁,也可以按照这么个模式去做,我最近比较喜欢用ipython来阅读python模块的代码,执行import gevent.monkey之后,只需要输入??gevent.monkey就可以查看源码了。

这个模块核心的函数其实就这几个,这些函数都位于模块的上方,get_original、patch_item、remove_item、patch_module还有一个全局变量叫做saved,默认指向一个空的字典对象。

首先来看patch_item函数的实现:

def patch_item(module, attr, newitem):
NONE = object()
olditem = getattr(module, attr, NONE)
if olditem is not NONE:
saved.setdefault(module.__name__, {}).setdefault(attr, olditem)
setattr(module, attr, newitem)
这个函数的功能就是从指定模块中查找旧的项,并把旧的项保存到saved字典中,然后将旧项替换成新项。

这里没有使用None,而是构建了一个空的object()作为默认属性,是NullPointer模式么?

然后是patch_module的实现:

def patch_module(name, items=None):
gevent_module = getattr(__import__('gevent.' + name), name)
module_name = getattr(gevent_module, '__target__', name)
module = __import__(module_name)
if items is None:
items = getattr(gevent_module, '__implements__', None)
if items is None:
raise AttributeError('%r does not have __implements__' % gevent_module)
for attr in items:
patch_item(module, attr, getattr(gevent_module, attr))

gevent有个约定,作为补丁的gevent模块要包含这两个属性,__target__和__implements__,__target__是被补丁的默认模块名称,可以不指定,默认为gevent子模块的名称,比如gevent.socket是socket模块的补丁,__implements__是要进行补丁的属性,这是gevent.socket模块中__implements__的定义:

# standard functions and classes that this module re-implements in a gevent-aware way:
__implements__ = ['create_connection',
'socket',
'SocketType',
'fromfd',
'socketpair']

patch_module的工作就是从gevent模块里面读取这两个属性,然后遍历调用patch_item进行替换。
可是有的时候我们不希望用补丁的东西,而是使用原先的模块去进行处理,该怎么办?前面提到过进行patch_item的时候会把旧的属性保存到名为saved的全局字典里面,如果要获得旧的模块属性,那么就要调用get_original函数从saved字典里面取出来。

In [6]: sleep = gevent.monkey.get_original("time", "sleep")
In [7]: sleep
Out[7]:
In [8]: import time
In [9]: time.sleep
Out[9]:
猴子补丁
猴子补丁的功能很强大,但是也带来了很多的风险,尤其是像gevent这种直接进行API替换的补丁,整个Python进程所使用的模块都会被替换,可能自己的代码能hold住,但是其它第三方库,有时候问题并不好排查,即使排查出来也是很棘手,所以,就像松本建议的那样,如果要使用猴子补丁,那么只是做功能追加,尽量避免大规模的API覆盖。
希望能帮到你,求采纳。
我要举报
如以上问答内容为低俗、色情、不良、暴力、侵权、涉及违法等信息,可以点下面链接进行举报!
大家都在看
关于足球队标
PS中关于图片放大不会模糊的设定……
杏田家私生活馆地址好找么,我有些事要过去
富锦聚福源旅馆地址在什么地方,我要处理点事
江苑招待所地址在哪,我要去那里办事
杏田家私(林州专卖店)地址在哪,我要去那里办
photoshop怎么画图?
请以导游的身份向游客介绍一大理
环球办公家具地址好找么,我有些事要过去
富锦聚福楼客栈地址在哪,我要去那里办事
7天优品酒店(胜利大街店)地址在哪,我要去那
简朴寨(团黄路店)地址在什么地方,我要处理点
三河市 列车表从唐山到三河市火车
鑫都旅馆(向阳路)地址好找么,我有些事要过去
南十六工商所地址在什么地方,想过去办事
推荐资讯
南平壕地址有知道的么?有点事想过去
五岁女孩突然变的爱发脾气是怎么回事
大塅里地址有知道的么?有点事想过去
梦幻西游盘丝怎么样秒的血才多?
南安普敦vs切尔西谁会赢,谢谢。尽量详细点
怎么把任务管理器里的进程调出来?
用简便方法计算274加194可能帮我解一解。
电子商务的重要性
由于工作进度太慢,副总大发脾气,说下个月别
花悦美容美体在什么地方啊,我要过去处理事情
古剑奇谭屠苏晴雪吻戏在哪一集
刘涛会粤语吗
手机登qq时,显示手机磁盘不足,清理后重新登
刺客的套装怎么选啊?