Python换出sys.MODULES并不像直觉上的那样工作

2022-03-31 00:00:00 python python-3.x python-import

问题描述

我正在尝试设置sys.modules词典,当时我正在研究another question的答案,我遇到了一些有趣的事情。链接的问题涉及删除导入模块的所有影响。基于another post,我想到了在导入之后从sys.modules中删除所有新模块的想法。我最初的实现是执行以下操作(使用numpy作为要加载和卸载的模块进行测试):

# Load the module
import sys
mod_copy = sys.modules.copy()
print('numpy' in mod_copy, 'numpy' in sys.modules) # False False
import numpy
print('numpy' in mod_copy, 'numpy' in sys.modules) # False True
print(id(numpy)) # 45138472

正如预期的那样,打印输出显示NumPy已成功导入,并且浅层副本不包含它。

现在我的想法是通过将mod_copy换回sys.modules来卸载模块,然后删除对该模块的本地引用。从理论上讲,这应该会删除对它的所有引用(可能确实如此):

sys.modules = mod_copy
del numpy
print('numpy' in sys.modules) # False

这应该足以重新导入模块,但当我这样做时

import numpy
print('numpy' in sys.modules) # False
print(id(numpy)) # 45138472
似乎没有重新加载NumPy模块,因为它具有与以前相同的id。它不会显示在sys.modules中,尽管import语句没有引发错误并且似乎成功完成(即,numpy模块存在于本地命名空间中)。

另一方面,我在my answer中对链接问题所做的实现似乎工作得很好。它直接修改词典,而不是将其换出:

import sys
mod_copy = sys.modules.copy()
print('numpy' in mod_copy, 'numpy' in sys.modules) # False False
import numpy
print('numpy' in mod_copy, 'numpy' in sys.modules) # False True
print(id(numpy)) # 35963432

for m in list(sys.modules):
    if m not in mod_copy:
        del sys.modules[m]
del numpy
print('numpy' in sys.modules) # False

import numpy
print('numpy' in sys.modules) # True
print(id(numpy)) # (54941000 != 35963432)

我在Anaconda安装上使用的是Python3.5.2。我最感兴趣的是关于Python3的解释,但我也对Python2.7+很好奇。

我能想到的唯一一件事是,sys维护另一个对sys.modules的引用,并使用该内部引用,而不管我对公共引用做了什么。不过,我不确定这是否涵盖了所有内容,所以我想知道到底发生了什么。


解决方案

即使在Python3.5中,导入实现的一部分也是still written in C,该部分使用PyThreadState_GET()->interp->modules来检索模块缓存,而不是通过sys.modules属性。您的导入正在通过这些代码路径之一在旧sys.modules中找到numpy

sys.modules不是为替换而设计的。docs提到替换它可能会出现意外行为:

这可以被操纵以强制重新加载模块和其他技巧。但是,替换词典不一定会按预期工作,从词典中删除重要项目可能会导致Python失败。

相关文章