Python 换出 sys.modules 并不像直觉那样有效
Python swapping out sys.modules does not work as intuited
我在尝试设置字典 sys.modules
的同时回答 another question and came across something interesting. The linked question deals with removing all the effects of importing a module. Based on 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 安装上使用 Python 3.5.2。我最感兴趣的是关于 Python 3 的解释,但我对 Python 2.7+ 也很好奇。
我唯一能想到的是,sys
维护了对 sys.modules
的另一个引用,并使用该内部引用,无论我对 public 做了什么.我不确定这是否涵盖了所有内容,所以我想知道到底发生了什么。
即使在 Python 3.5 中,部分导入实现也是 still written in C,那部分使用 PyThreadState_GET()->interp->modules
来检索模块缓存,而不是通过 sys.modules
属性。您的导入是通过这些代码路径之一在旧 sys.modules
中找到 numpy
。
sys.modules
不是为替换而设计的。 docs 提到替换它可能会出现意外行为:
This can be manipulated to force reloading of modules and other tricks. However, replacing the dictionary will not necessarily work as expected and deleting essential items from the dictionary may cause Python to fail.
我在尝试设置字典 sys.modules
的同时回答 another question and came across something interesting. The linked question deals with removing all the effects of importing a module. Based on 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 安装上使用 Python 3.5.2。我最感兴趣的是关于 Python 3 的解释,但我对 Python 2.7+ 也很好奇。
我唯一能想到的是,sys
维护了对 sys.modules
的另一个引用,并使用该内部引用,无论我对 public 做了什么.我不确定这是否涵盖了所有内容,所以我想知道到底发生了什么。
即使在 Python 3.5 中,部分导入实现也是 still written in C,那部分使用 PyThreadState_GET()->interp->modules
来检索模块缓存,而不是通过 sys.modules
属性。您的导入是通过这些代码路径之一在旧 sys.modules
中找到 numpy
。
sys.modules
不是为替换而设计的。 docs 提到替换它可能会出现意外行为:
This can be manipulated to force reloading of modules and other tricks. However, replacing the dictionary will not necessarily work as expected and deleting essential items from the dictionary may cause Python to fail.