python import 和from import 区别,以及在mock中的应用
前言
写python肯定会使用很多import 或者是from xxx import AAA
两者到底有什么区别呢?
关键原理
主要是 全局命名空间 的区别,全局命名空间可以用 globals() 函数查看。
全局命名空间虽然叫做全局,但是实际只是模块(一个模块,即是一个python文件)级别的命名空间。
区别表现
- 使用import xxx 到导入的是模块,导入后使用globals()看到xxx模块信息
- 使用from xxx import AAA导入的是对象(函数,类,变量),导入后使用globals()看到AAA信息
比如文件CCC.py:
import AAA
from BBB import funcB
print globals()
funcB()
就会有如下输出:
{
'AAA': < module 'AAA' from 'AAA.pyc' > ,
'funcB': < function funcB at 0x1d9b668 > ,
}
有什么意义?
其实这个时候,funcB,已经是模块CCC里的funcB了. 换句话说,在CCC文件的最后一行调用的funcB,实际是从CCC的全局命名空间里面找到了funcB这个函数。
更深层的说:如果这个时候对 BBB.funcB做出修改或扩展是不能够影响到CCC.funcB的,比如在使用Mock的时候。
常见场景
最常见的情况就是做单元测试的时候。
有文件CCC.py
import AAA
from BBB import funcB
def useFuncB():
return funcB()
def useFuncA():
return AAA.funcA()
现在需要对CCC里面的useFuncB和useFuncA做单元测试,但是AAA和BBB都还没有准备好,mock应该需要出现了
import CCC
class TestCCC(unittest.TestCase):
"""测试CCC"""
def test_userFuncB(self):
"""测试FuncB"""
with mock.patch("BBB.funcB") as mockobj:
mockobj.return_value = "your expect"
self.assertTrue(CCC.useFuncB() == "your expect")
def test_userFuncA(self):
"""测试FuncA"""
with mock.patch("AAA.funcA") as mockobj:
mockobj.return_value = "your expect"
self.assertTrue(CCC.useFuncA() == "your expect")
在如上代码中
- test_userFuncB 总是不能得到如期结果
- test_userFuncA 可以得到如期结果
原因就是: mock对象确实是修改BBB.funcB,但是CCC.useFuncB根本没有使用BBB.funcB,而是使用了CCC.funcB。
怎么办?
减少使用from xxx import BBB,特别是from xxx import * 这样的使用模式。后者将会污染全局命名空间
PS
上文的例子主要是为了介绍原理,并没有完整运行过。