Liv*_*viu 6 python unit-testing python-3.x python-importlib
这更多是为了理解 Python(在本例中为 3.9)的工作原理,而不是解决实际问题的努力,所以请耐心等待并忽略 m3 的荒谬方式。我只是想复制我正在处理的东西。
我有以下结构:
??? m1.py
??? m2
??? m3
??? __init__.py
??? m3.py
Run Code Online (Sandbox Code Playgroud)
m2/m3/初始化.py:
??? m1.py
??? m2
??? m3
??? __init__.py
??? m3.py
Run Code Online (Sandbox Code Playgroud)
m2/m3/m3.py:
from .m3 import *
Run Code Online (Sandbox Code Playgroud)
从现在开始,我将对 m1.py 进行更改
这是有效的,我期待它的工作:
def m3func():
print('m3 func is here')
Run Code Online (Sandbox Code Playgroud)
这并没有失败,所以它替换了 Mock 的模块。我也期待它的工作方式。
import m2.m3
m2.m3.m3func()
Run Code Online (Sandbox Code Playgroud)
同样的
import sys
from unittest.mock import Mock
sys.modules['m2.m3'] = Mock()
import m2.m3 as alias
alias.m3func()
Run Code Online (Sandbox Code Playgroud)
我不明白这里发生了什么:
import sys
from unittest.mock import Mock
sys.modules['m2.m3'] = Mock()
from m2 import m3
m3.m3func()
Run Code Online (Sandbox Code Playgroud)
m2.m3.m3func()
AttributeError: module 'm2' has no attribute 'm3'
Run Code Online (Sandbox Code Playgroud)
之间有什么区别import m2.m3,from m2 import m3还有import m2.m3 as alias
什么我不明白,有没有办法修复最后一个版本,这样它就不会抛出 AttributeError?我的示例 m2 是空的,但实际上,我不想完全换掉它,因为它确实包含我关心的东西。我只想针对 m3。是否有一个建议,尽可能最佳实践去使用这样的代码:m2.m3.m3func()?
小智 3
如果您还没有阅读过官方文档,您绝对应该阅读。
每当您使用 import 语句时,都会发生以下情况:
Python 在内部使用它sys.modules来搜索模块,并在发现模块和父模块时更新其条目。因此,当您 import 时m2.m3.m3,它会添加带有键“m2”、“m2.m3”、“m2.m3.m3”的条目来存储引用这些模块的对象。您可以使用以下函数sys.modules在每个语句之前/之后进行调试。
def print_status_relevant_modules():
import sys
print(sys.modules.get("m2", "<m2 not loaded>"))
print(sys.modules.get("m2.m3", "<m2.m3 not loaded>"))
print(sys.modules.get("m2.m3.m3", "<m2.m3.m3 not loaded>"))
print()
Run Code Online (Sandbox Code Playgroud)
每当 Python 发现一个新包(__init__.py其中包含文件的目录)或module.py模块时,它也会处理该模块。在这种情况下,当Python发现模块 的存在时m2.m3,就会执行 的内容m2/m3/__init__.py,从而执行 import 语句from .m3 import *,从而导致模块的发现m2.m3.m3(并引入局部变量m3func)m2.m3。
当开始模拟 sys.modules 中带有键“m2.m3”的条目时,您的问题就开始了,因为现在您已经中断了 Python 搜索模块的过程。因为你事先模拟了 sys.modules 中模块的条目m2.m3,Python 认为它已经处理了这个模块,所以 Python 永远不会执行它的__init__.py文件。结果,m2.m3.m3永远不会被发现,不会添加任何条目,并且 for 的局部变量m3func永远不会被引入到 中m2.m3。
如果您想知道为什么在模拟模块的条目时没有看到任何错误,即使您正在调用m3func(),这是因为模拟将接受任何调用,期望您稍后验证是否进行了某个调用。
所有不同 import 语句之间的最大区别在于引入了哪些局部变量:
import m2.m3结果是一个m2带有属性的局部变量m3;引入的变量引用代表模块的 Module 实例m2from m2 import m3结果为局部变量m3;引入的变量引用代表模块的 Module 实例m2.m3import m2.m3 as alias结果为局部变量alias;引入的变量引用代表模块的 Module 实例m2.m3您可以在任何地方使用该语句print(dir())来查看定义了哪些局部变量,或者在对象上使用该语句。
print(dir()) # m2 is not defined
import m2.m3
print(dir()) # m2 is defined
print(dir(m2)) # shows that m2 has an attribute m3
Run Code Online (Sandbox Code Playgroud)
作为额外的好处,语句from .m3 import *inm2/m3/__init__.py会导致 的所有局部变量m2.m3.m3被导入到m2.m3。m3func在这种情况下,仅添加变量。
当使用 import 语句时,会引入import m2.m3局部变量,该变量引用代表模块 m2 的实例。在搜索模块时,Python 应该已经发现了模块 m2.m3,并将该属性添加到表示模块 m2 的实例中,以引用表示模块 m2.m3 的实例。但是,因为您事先进行了模拟,所以永远不会发现模块 m2.m3,因此该属性永远不会添加到表示模块 m2 的实例中。当您尝试访问.m2Modulem3ModuleModulesys.modules['m2.m3']m3Modulem2.m3
当您使用 import 语句时,引入的import m2.m3 as alias局部变量引用代表模块 m3 的实例。但是,因为您事先进行了模拟,Python 认为它已经发现了模块 m2.m3,并返回 的值。因此,变量最终引用实例而不是表示模块 m2.m3 的实例,并且您不会收到任何错误,因为实例接受所有调用。aliasModulesys.modules['m2.m3']sys.modules['m2.m3']aliasMockModuleMock
当你使用 import 语句时也会发生同样的事情from m2 import m3;该变量m3最终将引用该Mock实例。
据我所知,你把Python的导入系统弄乱了,以至于你不能再依赖它“只使用”m2.m3或m2.m3.m3. Python 会找到一种方式以这样或那样的方式进行抱怨。
这可能是一种情况,实际问题是设计问题,而模拟永远不会是正确的答案,从长远来看只会导致更多问题,但是,我不知道实际情况是什么。但是,您应该尝试找到一种方法来避免整个情况。
| 归档时间: |
|
| 查看次数: |
116 次 |
| 最近记录: |