强制从源中卸载/解构动态导入的文件

时间:2016-10-08 00:22:09

标签: python python-3.x python-importlib

一直是SO的长期浏览器,最后问我自己的问题!

所以,我正在编写一个自动化脚本/模块,它以递归方式查找具有特定名称的python模块的目录。如果我找到一个具有该名称的模块,我会动态加载它,从中获取我需要的内容,然后将其卸载。我注意到,虽然简单地对模块进行了删除并没有删除对该模块的所有引用,但是在某个地方还有另一个延迟,我不知道它在哪里。我试着看一下源代码,但是不能理解它。以下是我所看到的一个示例,大大简化了:

我正在使用Python 3.5.2(Anaconda v4.2.0)。我正在使用importlib,这就是我想要坚持的。我也希望能够用vanilla python-3做到这一点。

我从python docs here获得了源代码的导入(是的,我知道这是Python 3.6文档)。

我的主要推动者......

# main.py
import importlib.util
import sys

def foo():
   spec = importlib.util.spec_from_file_location('a', 'a.py')
   module = importlib.util.module_from_spec(spec)
   spec.loader.exec_module(module)
   print(sys.getrefcount(module))
   del module
   del spec

if __name__ == '__main__':
   foo()
   print('THE END')

我的样本模块......

# a.py
print('hello from a')

class A():
   def __del__(self):
      print('SO LONG A!')

inst = A()

输出:

python main.py
HELLO FROM A!
2
THE END
SO LONG A!

我希望看到“太长了!”在“THE END”之前打印。那么,这个对我的模块的其他隐藏引用在哪里?我明白我的del是无偿的,因为我把它包含在一个函数中。我只是希望删除和范围是明确的。如何让a.py完全卸载?我计划动态加载像a.py这样的大量模块,而且我不想再坚持使用它们了。有什么我想念的吗?

1 个答案:

答案 0 :(得分:0)

这里有一个循环引用,模块对象引用再次引用该模块的对象。

这意味着模块不会立即清除(因为引用计数永远不会自动变为0)。您需要等待垃圾收集器破坏圆圈。

您可以致电gc.collect()强制执行此操作:

import gc

# ...

if __name__ == '__main__':
   foo()
   gc.collect()
   print('THE END')

有了这个,输出变为:

$ python main.py
hello from a
2
SO LONG A!
THE END