0

一直是SO的长期浏览器,终于问了我自己的问题!

所以,我正在编写一个自动化脚本/模块,它递归地在目录中查找具有特定名称的 python 模块。如果我找到一个具有该名称的模块,我会动态加载它,从中提取我需要的内容,然后卸载它。我注意到虽然简单地删除模块并不会删除对该模块的所有引用,但在某个地方还有另一个挥之不去的地方,我不知道它在哪里。我尝试查看源代码,但无法很好地理解它。这是我所看到的示例,大大简化了:

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

我从这里的 python 文档中获得了源代码的导入(是的,我知道这是 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!

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

4

1 回答 1

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
于 2016-10-08T00:46:12.157 回答