为何unittest的mock.patch.start会重复执行启动补丁的函数?
Python Mock 模块加载异常问题解析
场景重现
我们有两个Python文件:
to_patch.py
from unittest.mock import patch def patch_a_function(): print("Patching!") patcher = patch("to_be_patched.function") patcher.start() print("Done patching!")
to_be_patched.py
from to_patch import patch_a_function def function(): pass patch_a_function() function()
运行命令 python -m to_be_patched 后,输出如下:
Patching! Patching!
疑问点
- 为何
Done patching!从未被打印? - 为何
Patching!被打印两次?
提问者补充排查信息:调用patch.start似乎会再次触发patch_a_function执行,猜测这与to_be_patched.py中导入该函数有关,但不确定函数二次执行的原因;同时也不清楚为何两次调用patch_a_function都未执行到Done patching!代码行,patcher.start()应该不会阻塞,因为程序能正常退出而非挂起。
编辑补充:看起来没人能复现Done patching!不打印的情况——这应该是本地环境的问题。
问题解析
1. Patching!打印两次的原因
这是循环导入+Mock的模块加载机制共同导致的:
- 执行
python -m to_be_patched时,Python首先加载to_be_patched模块,当执行到from to_patch import patch_a_function时,会转而加载to_patch模块。 to_patch模块加载完成后,回到to_be_patched继续执行顶层代码,调用patch_a_function(),打印第一次Patching!。- 当执行
patcher.start()时,unittest.mock.patch会尝试导入目标模块to_be_patched以定位要替换的function对象。但此时to_be_patched模块尚未完成完整加载,Python会重新执行该模块的顶层代码——于是又会导入patch_a_function并再次调用,打印第二次Patching!。
2. Done patching!未打印的问题
根据补充说明,其他环境无法复现该现象,大概率是本地Python环境、unittest.mock版本差异,或者模块缓存(__pycache__文件夹)问题导致的。正常情况下两次patch_a_function调用都应该执行到print("Done patching!"),可以尝试清理模块缓存或切换干净的虚拟环境测试。
内容的提问来源于stack exchange,提问作者AmagicalFishy
相关产品推荐
相关产品推荐

