由调用模块污染的Python模块导入

时间:2016-01-07 22:31:04

标签: python mocking python-import python-module

我创建了模块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导入BmockB并使用mockB修补B,然后调用B.foo()。一切都按预期工作 - 打印'mock_foo()'

当模块A导入模块C时,会出现奇怪现象,模块B也是A的客户端,CC.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语句更改为适当的函数调用)。

1 个答案:

答案 0 :(得分:2)

这完全是预期的行为。

模块是单身人士。内存中只有一个副本(存储在sys.modules中)。导入加载模块一次,然后在导入的任何地方重用它。

因此,只有一个 B.foo对象,并将其替换为另一个函数。其他任何地方都使用B.foo来查找对函数的引用,因此它们会看到被替换的对象。

如果您需要暂时模拟某些内容,则需要确保在完成后重新启动原始对象。 {3}}(Python 3中的mock library)可以为您完成此任务。

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

from B import foo

foo()

您创建了对foo本身的新引用。稍后替换B.foo不会改变此“本地”foo引用。