一个猴子如何在python中修补一个函数?

gui*_*ism 76 python monkeypatching

我在使用另一个功能从另一个模块替换功能时遇到了麻烦,这让我发疯了.

假设我有一个模块bar.py,如下所示:

from a_package.baz import do_something_expensive

def a_function():
    print do_something_expensive()
Run Code Online (Sandbox Code Playgroud)

我有另一个模块,看起来像这样:

from bar import a_function
a_function()

from a_package.baz import do_something_expensive
do_something_expensive = lambda: 'Something really cheap.'
a_function()

import a_package.baz
a_package.baz.do_something_expensive = lambda: 'Something really cheap.'
a_function()
Run Code Online (Sandbox Code Playgroud)

我希望得到结果:

Something expensive!
Something really cheap.
Something really cheap.
Run Code Online (Sandbox Code Playgroud)

但相反,我得到了这个:

Something expensive!
Something expensive!
Something expensive!
Run Code Online (Sandbox Code Playgroud)

我究竟做错了什么?

Nic*_*ley 85

考虑Python命名空间的工作原理可能有所帮助:它们本质上是字典.所以当你这样做时:

from a_package.baz import do_something_expensive
do_something_expensive = lambda: 'Something really cheap.'
Run Code Online (Sandbox Code Playgroud)

想到这样:

do_something_expensive = a_package.baz['do_something_expensive']
do_something_expensive = lambda: 'Something really cheap.'
Run Code Online (Sandbox Code Playgroud)

希望你能明白为什么这不工作,那么:-)一旦导入一个名称命名空间,这个名字在你导入的命名空间值无关.您只是在本地模块的命名空间或上面的a_package.baz命名空间中修改do_something_expensive的值.但是因为bar直接导入do_something_expensive,而不是从模块命名空间引用它,所以需要写入其命名空间:

import bar
bar.do_something_expensive = lambda: 'Something really cheap.'
Run Code Online (Sandbox Code Playgroud)


Rya*_*cox 21

这里有一个非常优雅的装饰:Guido van Rossum:Python-Dev列表:Monkeypatching Idioms.

还有dectools包,我看到了PyCon 2010,它也可以在这个上下文中使用,但实际上可能会采用另一种方式(在方法声明级别monkeypatching ...你不在哪里)

  • 那些装饰器似乎不适用于这种情况. (4认同)

Ris*_*nha 8

如果您只想为您的呼叫修补它,否则保留原始代码,您可以使用https://docs.python.org/3/library/unittest.mock.html#patch(自 Python 3.3 起):

with patch('a_package.baz.do_something_expensive', new=lambda: 'Something really cheap.'):
    print do_something_expensive()
    # prints 'Something really cheap.'

print do_something_expensive()
# prints 'Something expensive!'
Run Code Online (Sandbox Code Playgroud)