为什么sys.modules中有虚拟模块?

18 投票
1 回答
1980 浏览
提问于 2025-04-15 17:20

导入标准的“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 向绝对导入的方向发展,这种混乱的情况会减少。

撰写回答