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 ...你不在哪里)
如果您只想为您的呼叫修补它,否则保留原始代码,您可以使用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)