为什么sys.modules中有虚拟模块?
导入标准的“logging”模块会让sys.modules里多出一堆无用的条目:
Python 2.5.4 (r254:67916, Dec 23 2008, 15:10:54) [MSC v.1310 32 bit (Intel)] on win32
>>> import sys
>>> import logging
>>> sorted(x for x in sys.modules.keys() if 'log' in x)
['logging', 'logging.atexit', 'logging.cStringIO', 'logging.codecs',
'logging.os', 'logging.string', 'logging.sys', 'logging.thread',
'logging.threading', 'logging.time', 'logging.traceback', 'logging.types']
# and perhaps even more surprising:
>>> import traceback
>>> traceback is sys.modules['logging.traceback']
False
>>> sys.modules['logging.traceback'] is None
True
所以,当你导入这个包时,它会在sys.modules里增加一些额外的名字,但这些名字并不是模块,而只是指向None的引用。其他模块(比如xml.dom和encodings)也有这个问题。为什么会这样呢?
补充:在bobince的回答基础上,有一些页面描述了这个特性的起源(可以查看“sys.modules中的虚假条目”这一节)和未来。
1 个回答
24
在 sys.modules
中的 None
值表示相对导入失败的缓存记录。
举个例子,当你在包 foo
中并且使用 import sys
时,Python 首先会去找 foo.sys
模块。如果找不到,它就会去顶层的 sys
模块。为了避免在后续的相对导入中再次检查文件系统是否有 foo/sys.py
,Python 会在 sys.modules
中存储 None
,这样就标记了这个模块不存在,后续的导入就不会再去找这里,而是直接使用已经加载的 sys
。
这是 cPython 的一个实现细节,虽然你不能完全依赖它,但如果你在做一些复杂的导入或重载操作时,了解这个细节是有帮助的。
这种情况发生在所有包中,而不仅仅是 logging
。比如,你可以尝试 import xml.dom
,然后在模块列表中看到 xml.dom.xml
,因为它在尝试从 xml.dom
中导入 xml
。
随着 Python 向绝对导入的方向发展,这种混乱的情况会减少。