Python 模块导入被调用模块污染

Python module imports polluted by calling module

我创建了模块 B,它在其函数 foo() 中进行了一些昂贵的网络调用。所以我创建了一个模块 mockB 用于测试。 mockB 有一个名为 mockfoo() 的方法,它是 B.foo() 的模拟,另一个名为 patch() 的方法,它以模块 B 的实例作为参数,并且用 mockfoo().

覆盖它的 foo() 方法

B.py

def foo():
    print 'foo()'

模拟B.py

def patch(B_module):
    B_module.foo = mock_foo

def mock_foo():
    print 'mock_foo()'

模块 A 导入 BmockB 并使用 mockB 修补 B,然后调用 B.foo()。一切都按预期工作 - 'mock_foo()' 被打印出来。

当模块 A 导入模块 C 时出现奇怪,它也是 B 的客户端,并且既不是 A 也不是 C 补丁C.B。出于某种原因,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 中(打印语句根据需要更改为函数调用)。

这完全是预期的行为。

模块是单例。内存中只有一份(存放在sys.modules)。导入加载模块一次,然后在导入的任何地方重复使用它。

因此,只有 一个 B.foo 对象,您将其替换为另一个函数。其他地方仍然使用 B.foo 来查找对函数的引用,所以他们看到被替换的对象。

如果您需要临时模拟某些东西,您需要确保在完成后 re-instate 原始对象。 Python 3) 中的 mock library (unittest.mock 可以为您做到这一点。

或者,创建对函数对象的本地引用。如果您使用:

from B import foo

foo()

您创建了对 foo 本身的新引用。稍后替换 B.foo 不会改变此 'local' foo 参考。