3

在 Python 中,该术语monkey patch仅指在运行时对类或模块的动态修改,作为初学者,我很难在 python 的上下文中理解该术语。谁能用一个真实的例子向我解释我们到底是怎么做的?

  1. 类的动态修改
  2. 在运行时动态修改模块

我坚持一个真实世界的例子(尽可能简单)来了解我们必须在哪些场景中执行此类任务?

4

2 回答 2

6

Monkey-patching 是一种以现有代码将继续运行但行为已修改的方式进行一些全局底层更改的方法。

str一个改变内置命令行为的非常简单的例子:

b.py

def foo(msg):
    s = str(msg)
    print s, type(s)

一个.py

import b

b.foo('foo')

# monkey-patch 
import __builtin__
__builtin__.str = unicode

b.foo('foo')

# Results:
#foo <type 'str'>
#foo <type 'unicode'>

a模块使用该str命令修改了其他代码的行为,通过修补它来unicode代替使用。这是必要的,因为我们假装我们无法访问b.py的代码。它可能是一个巨大的包,我们只是使用而无法更改。但是我们可以插入新的代码来改变行为。

来自 gevent的真实示例

>>> import gevent
>>> from gevent import socket
>>> urls = ['www.google.com', 'www.example.com', 'www.python.org']
>>> jobs = [gevent.spawn(socket.gethostbyname, url) for url in urls]
>>> gevent.joinall(jobs, timeout=2)
>>> [job.value for job in jobs]
['74.125.79.106', '208.77.188.166', '82.94.164.162']  

上面的示例使用 gevent.socket 进行套接字操作。如果使用标准套接字模块,则需要 3 倍的时间才能完成,因为 DNS 请求是连续的。在 greenlets 中使用标准的 socket 模块会使 gevent 变得毫无意义,那么构建在 socket 之上的模块和包呢?

这就是猴子修补的目的。gevent.monkey 中的函数小心地将标准套接字模块中的函数和类替换为它们的对应物。这样,即使是不知道 gevent 的模块也可以从在 multi-greenlet 环境中运行中受益。

>>> from gevent import monkey; monkey.patch_socket()
>>> import urllib2 # it's usable from multiple greenlets now
于 2012-08-15T21:18:42.083 回答
3

使用猴子补丁时有一些现实生活中的例子:

  1. 单元测试。创建存根/模拟修补真实对象以更改其行为以尊重测试需求通常很方便。例如,您想测试Object.method1()行为。方法可以返回一个字符串或无。所以这里的猴子补丁来了。您替换method1为只包含一行代码的存根方法return None
  2. 在运行时修改/扩展代码。这种用例的最佳示例可能是相当流行的 python gevent 库。它使用猴子补丁来使标准套接字模块按照 gevent 需要的方式工作。这是一个源代码
  3. 在运行时将补丁应用于对象。对对象进行简单快速的修改会很有用,在此之后,对象必须符合应用程序其他部分使用的某些接口。它可以在 Python 中使用setattr函数轻松完成。好吧,这通常意味着您的应用程序架构不好,并且不是一个好的设计决策
于 2012-08-15T21:18:50.840 回答