Monkey patch
What is Monkey Patch
Monkey patch就是在运行时对已有的代码进行修改,达到hot patch的目的。Eventlet中大量使用了该技巧,以替换标准库中的组件,比如socket。首先来看一下最简单的monkey patch的实现。
class Foo(object):def bar(self):print 'Foo.bar'def bar(self):print 'Modified bar'Foo().bar()Foo.bar = barFoo().bar()
由于Python中的名字空间是开放,通过dict来实现,所以很容易就可以达到patch的目的。
Python namespace
Python有几个namespace,分别是
- locals
- globals
- builtin
其中定义在函数内声明的变量属于locals,而模块内定义的函数属于globals。
Python module Import & Name Lookup
当我们import一个module时,python会做以下几件事情
- 导入一个module
- 将module对象加入到sys.modules,后续对该module的导入将直接从该dict中获得
- 将module对象加入到globals dict中
当我们引用一个模块时,将会从globals中查找。这里如果要替换掉一个标准模块,我们得做以下两件事情
- 将我们自己的module加入到sys.modules中,替换掉原有的模块。如果被替换模块还没加载,那么我们得先对其进行加载,否则第一次加载时,还会加载标准模块。(这里有一个import hook可以用,不过这需要我们自己实现该hook,可能也可以使用该方法hook module import)
- 如果被替换模块引用了其他模块,那么我们也需要进行替换,但是这里我们可以修改globals dict,将我们的module加入到globals以hook这些被引用的模块。
Eventlet Patcher Implementation
现在我们先来看一下eventlet中的Patcher的调用代码吧,这段代码对标准的ftplib做monkey patch,将eventlet的GreenSocket替换标准的socket。
from eventlet import patcher# *NOTE: there might be some funny business with the "SOCKS" module
# if it even still exists
from eventlet.green import socketpatcher.inject('ftplib', globals(), ('socket', socket))del patcher
inject函数会将eventlet的socket模块注入标准的ftplib中,globals dict被传入以做适当的修改。
让我们接着来看一下inject的实现。
__exclude = set(('__builtins__', '__file__', '__name__'))def inject(module_name, new_globals, *additional_modules):"""Base method for "injecting" greened modules into an imported module. Itimports the module specified in *module_name*, arranging things sothat the already-imported modules in *additional_modules* are used when*module_name* makes its imports.*new_globals* is either None or a globals dictionary that gets populatedwith the contents of the *module_name* module. This is useful when creatinga "green" version of some other module.*additional_modules* should be a collection of two-element tuples, of theform (, ). If it's not specified, a default selection ofname/module pairs is used, which should cover all use cases but may beslower because there are inevitably redundant or unnecessary imports."""if not additional_modules:# supply some defaultsadditional_modules = (_green_os_modules() +_green_select_modules() +_green_socket_modules() +_green_thread_modules() +_green_time_modules())## Put the specified modules in sys.modules for the duration of the importsaved = {}for name, mod in additional_modules:saved[name] = sys.modules.get(name, None)sys.modules[name] = mod## Remove the old module from sys.modules and reimport it while## the specified modules are in placeold_module = sys.modules.pop(module_name, None)try:module = __import__(module_name, {}, {}, module_name.split('.')[:-1])if new_globals is not None:## Update the given globals dictionary with everything from this new modulefor name in dir(module):if name not in __exclude:new_globals[name] = getattr(module, name)## Keep a reference to the new module to prevent it from dyingsys.modules['__patched_module_' + module_name] = modulefinally:## Put the original module backif old_module is not None:sys.modules[module_name] = old_moduleelif module_name in sys.modules:del sys.modules[module_name]## Put all the saved modules backfor name, mod in additional_modules:if saved[name] is not None:sys.modules[name] = saved[name]else:del sys.modules[name]return module
注释比较清楚的解释了代码的意图。代码还是比较容易理解的。这里有一个函数__import__,这个函数提供一个模块名(字符串),来加载一个模块。而我们import或者reload时提供的名字是对象。
if new_globals is not None:## Update the given globals dictionary with everything from this new modulefor name in dir(module):if name not in __exclude:new_globals[name] = getattr(module, name)
这段代码的作用是将标准的ftplib中的对象加入到eventlet的ftplib模块中。因为我们在eventlet.ftplib中调用了inject,传入了globals,而inject中我们手动__import__了这个module,只得到了一个模块对象,所以模块中的对象不会被加入到globals中,需要手动添加。
这里为什么不用from ftplib import *的缘故,应该是因为这样无法做到完全替换ftplib的目的。因为from … import *会根据__init__.py中的__all__列表来导入public symbol,而这样对于下划线开头的private symbol将不会导入,无法做到完全patch。
Monkey patch相关推荐
- python运行时修改代码会怎样_python运行时修改代码的方法——monkey patch
monkey patch (猴子补丁) 用来在运行时动态修改已有的代码,而不需要修改原始代码. 简单的monkey patch 实现: [Python] #coding=utf-8 def origi ...
- python 猴子补丁_python面试题精讲——monkey patch(猴子补丁)
前言本次依然是选自python面试题系列,将一个比较偏的概念,可能很多人没怎么听说过--猴子补丁,其实所讲的内容很简单,它得益于python灵活的语法.一切皆对象的思想,一起来看看看看吧!目录一.什么 ...
- Python中的Monkey Patch(猴子补丁)
一.猴子补丁的介绍 关于猴子补丁为啥叫猴子补丁,据说是这样子的: 这个叫法起源于Zope框架,大家在修正Zope的Bug的时候经常在程序后面追加更新部分,这些被称作是"杂牌军补丁(gueri ...
- python猴子补丁_Python猴子补丁Monkey Patch用法实例解析
属性在运行时的动态替换,叫做猴子补丁(Monkey Patch). 为什么叫猴子补丁 属性的运行时替换和猴子也没什么关系,关于猴子补丁的由来网上查到两种说法: 1.这个词原来为Guerrilla Pa ...
- 什么是monkey patch(猴子补丁)
monkey patch指的是在运行时动态替换,一般是替换(添加)类的方法.类或者模块都可以 首先使用系统模块的socket,打印socket.socket函数 然后在使用gevent模块的monke ...
- monkey patch(猴子补丁)
一.什么是monkey patch 在网上也查了一下,关于这个名字起的比较随意,也勉强理解这样吧: 这个词原来叫Guerrilla Patch,杂牌军.游击队,说明这部分不是原装的,在英文里面guer ...
- 什么是猴子补丁(monkey patch)
monkey patch指的是在运行时动态替换,一般是在startup的时候. 用过gevent就会知道,会在最开头的地方gevent.monkey.patch_all();把标准库中的thread/ ...
- python画猴子_Python猴子补丁Monkey Patch用法实例解析
属性在运行时的动态替换,叫做猴子补丁(Monkey Patch). 为什么叫猴子补丁 属性的运行时替换和猴子也没什么关系,关于猴子补丁的由来网上查到两种说法: 1.这个词原来为Guerrilla Pa ...
- 在 Go 语言中 Patch 非导出函数
TLDR; 使用 supermonkey[1] 可以 patch 任意导出/非导出函数. 目前在 Go 语言里写测试还是比较麻烦的. 除了传统的 test double,也可以通过把一个现成的对象的成 ...
最新文章
- SAP MM 条件类型中PB00的‘Group Cond.‘标记的作用?
- python numpy转字符串
- C语言 联合体使用技巧之位带操作
- DL之CNN可视化:利用SimpleConvNet算法【3层,im2col优化】基于mnist数据集训练并对卷积层输出进行可视化
- h5应用数据加密_邦伲德H5场景个性化开发,打破传统营销局限
- 光纤收发器在使用过程中有哪些需要注意的事项?
- 基于tcp的应用层协议还原
- “支付功能”怎么测试?
- 二、RabbitMQ常用交换器
- 「知识蒸馏」最新2022研究综述
- windows php_redis.dll 官方下载地址 php5x php7x
- 计算机网络课程设计小型企业局域网的组建,计算机网络课程设计小型企业局域网的组建.doc...
- 博科FC光纤交换机详细配置教程
- ss下,解决ncurl: (7) Failed to connect to android.googlesource.com port 443: Connection refused
- c语言日历程序实验报告,万年历实验报告.doc
- unity 敌人朝向主角
- banner图的开发
- Redis 缓存穿透、击穿、雪崩现象及解决方案
- NGINX配置以及优化
- Aurora8B10B IP使用 -01- 简介与端口描述