我创建了模块B
,它在其函数foo()
中执行了一些昂贵的网络调用。所以我创建了一个模块mockB
进行测试。 mockB
有一个名为mockfoo()
的方法,它是B.foo()
的模拟,另一个名为patch()
,它将模块B
的一个实例作为参数,使用foo()
覆盖其mockfoo()
方法。
B.py
def foo():
print 'foo()'
mockB.py
def patch(B_module):
B_module.foo = mock_foo
def mock_foo():
print 'mock_foo()'
模块A
导入B
和mockB
并使用mockB
修补B
,然后调用B.foo()
。一切都按预期工作 - 打印'mock_foo()'
。
当模块A
导入模块C
时,会出现奇怪现象,模块B
也是A
的客户端,C
和C.B
补丁{{} 1}}。出于某种原因,C.do_B_thing()
打印'mock_foo()'
。
C.py
import B
def do_B_thing():
B.foo()
A.py
import B
import mockB
import C
from B import foo
mockB.patch(B)
# Unsurprising
print 'Calling patched B.foo():'
B.foo()
# Surprising
print 'Module C calling unpatched B.foo():'
C.do_B_thing()
# For comparison
print 'Module C calling local foo():'
foo()
输出:
$ python A.py
Calling patched B.foo():
mock_foo()
Module C calling unpatched B.foo():
mock_foo()
Module C calling local foo():
foo()
在Python 2.7和3中都会发生(将print语句更改为适当的函数调用)。
答案 0 :(得分:2)
这完全是预期的行为。
模块是单身人士。内存中只有一个副本(存储在sys.modules
中)。导入加载模块一次,然后在导入的任何地方重用它。
因此,只有一个 B.foo
对象,并将其替换为另一个函数。其他任何地方都使用B.foo
来查找对函数的引用,因此它们会看到被替换的对象。
如果您需要暂时模拟某些内容,则需要确保在完成后重新启动原始对象。 {3}}(Python 3中的mock
library)可以为您完成此任务。
或者,创建对函数对象的本地引用。如果您使用:
from B import foo
foo()
您创建了对foo
本身的新引用。稍后替换B.foo
不会改变此“本地”foo
引用。