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中查找。这里如果要替换掉一个标准模块,我们得做以下两件事情

  1. 将我们自己的module加入到sys.modules中,替换掉原有的模块。如果被替换模块还没加载,那么我们得先对其进行加载,否则第一次加载时,还会加载标准模块。(这里有一个import hook可以用,不过这需要我们自己实现该hook,可能也可以使用该方法hook module import)
  2. 如果被替换模块引用了其他模块,那么我们也需要进行替换,但是这里我们可以修改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相关推荐

  1. python运行时修改代码会怎样_python运行时修改代码的方法——monkey patch

    monkey patch (猴子补丁) 用来在运行时动态修改已有的代码,而不需要修改原始代码. 简单的monkey patch 实现: [Python] #coding=utf-8 def origi ...

  2. python 猴子补丁_python面试题精讲——monkey patch(猴子补丁)

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

  3. Python中的Monkey Patch(猴子补丁)

    一.猴子补丁的介绍 关于猴子补丁为啥叫猴子补丁,据说是这样子的: 这个叫法起源于Zope框架,大家在修正Zope的Bug的时候经常在程序后面追加更新部分,这些被称作是"杂牌军补丁(gueri ...

  4. python猴子补丁_Python猴子补丁Monkey Patch用法实例解析

    属性在运行时的动态替换,叫做猴子补丁(Monkey Patch). 为什么叫猴子补丁 属性的运行时替换和猴子也没什么关系,关于猴子补丁的由来网上查到两种说法: 1.这个词原来为Guerrilla Pa ...

  5. 什么是monkey patch(猴子补丁)

    monkey patch指的是在运行时动态替换,一般是替换(添加)类的方法.类或者模块都可以 首先使用系统模块的socket,打印socket.socket函数 然后在使用gevent模块的monke ...

  6. monkey patch(猴子补丁)

    一.什么是monkey patch 在网上也查了一下,关于这个名字起的比较随意,也勉强理解这样吧: 这个词原来叫Guerrilla Patch,杂牌军.游击队,说明这部分不是原装的,在英文里面guer ...

  7. 什么是猴子补丁(monkey patch)

    monkey patch指的是在运行时动态替换,一般是在startup的时候. 用过gevent就会知道,会在最开头的地方gevent.monkey.patch_all();把标准库中的thread/ ...

  8. python画猴子_Python猴子补丁Monkey Patch用法实例解析

    属性在运行时的动态替换,叫做猴子补丁(Monkey Patch). 为什么叫猴子补丁 属性的运行时替换和猴子也没什么关系,关于猴子补丁的由来网上查到两种说法: 1.这个词原来为Guerrilla Pa ...

  9. 在 Go 语言中 Patch 非导出函数

    TLDR; 使用 supermonkey[1] 可以 patch 任意导出/非导出函数. 目前在 Go 语言里写测试还是比较麻烦的. 除了传统的 test double,也可以通过把一个现成的对象的成 ...

最新文章

  1. SAP MM 条件类型中PB00的‘Group Cond.‘标记的作用?
  2. python numpy转字符串
  3. C语言 联合体使用技巧之位带操作
  4. DL之CNN可视化:利用SimpleConvNet算法【3层,im2col优化】基于mnist数据集训练并对卷积层输出进行可视化
  5. h5应用数据加密_邦伲德H5场景个性化开发,打破传统营销局限
  6. 光纤收发器在使用过程中有哪些需要注意的事项?
  7. 基于tcp的应用层协议还原
  8. “支付功能”怎么测试?
  9. 二、RabbitMQ常用交换器
  10. 「知识蒸馏」最新2022研究综述
  11. windows php_redis.dll 官方下载地址 php5x php7x
  12. 计算机网络课程设计小型企业局域网的组建,计算机网络课程设计小型企业局域网的组建.doc...
  13. 博科FC光纤交换机详细配置教程
  14. ss下,解决ncurl: (7) Failed to connect to android.googlesource.com port 443: Connection refused
  15. c语言日历程序实验报告,万年历实验报告.doc
  16. unity 敌人朝向主角
  17. banner图的开发
  18. Redis 缓存穿透、击穿、雪崩现象及解决方案
  19. NGINX配置以及优化
  20. Aurora8B10B IP使用 -01- 简介与端口描述

热门文章

  1. 如何评价2018字节跳动(今日头条)ACM-ICPC冬令营?
  2. Ps笔刷:划痕噪音效果
  3. 中文转拼音 (utf8版,gbk转utf8也可用)
  4. java登录注册抽奖完整代码_JAVA实现用户抽奖功能(附完整代码)
  5. 计算机网络连接在哪,本地连接在哪里找【四种方法】
  6. 数据为王的时代,NLP数据产业的未来
  7. 制作自己的手机短信验证码
  8. 分析型数据库 AnalyticDB学习 ----基本介绍
  9. Android--静默安装和静默卸载
  10. php中 prefix,linux中--prefix命令是什么意思?