当 Python 解释器在标准模式下启动时, 一些模块会被解释器自动导入, 用于系统相关操作。例如内建的模块是被自动导入的,一些内建的函数可以直接使用。内建的模块是 __builtin__ , 在 Python3.x 中已经被替换成了 builtins,但是 __buitins__ 同时存在与 python2.x 和 python3.x 中。__buitins__ 是对内建模块的引用,无论任何地方要想使用内建模块,都必须在该位置所处的作用域中导入内建模块,而对于 __builtins__ 却不用导入,它在任何模块都直接可见。

sys.modules 变量包含一个由当前载入(完整且成功导入)到解释器的模块组成的字典, 模块名作为键, 它们的位置作为值。如果在刚启动 python 解释器时做如下的操作:

Python 2.7.6 (default, Jun 22 2015, 17:58:13)
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.modules.keys()
['copy_reg', 'sre_compile', '_sre', 'encodings', 'site', '__builtin__', 'sysconfig', '__main__', 'encodings.encodings', 'abc', 'posixpath', '_weakrefset', 'errno', 'encodings.codecs', 'sre_constants', 're', '_abcoll', 'types', '_codecs', 'encodings.__builtin__', '_warnings', 'genericpath', 'stat', 'zipimport', '_sysconfigdata', 'warnings', 'UserDict', 'encodings.utf_8', 'sys', 'codecs', 'readline', '_sysconfigdata_nd', 'os.path', 'sitecustomize', 'signal', 'traceback', 'linecache', 'posix', 'encodings.aliases', 'exceptions', 'sre_parse', 'keyrings', 'os', '_weakref']

其中,sys.modules.keys() 所列出的模块就是解释器在启动时自动加载的模块,这里我们会发现像一些我们熟知的模块已经被自动加载了,例如 os、re、errno 等。但是,我们在解释器中却不能直接使用 os 模块,这是何原因?

sys.modules.keys() 中的每个模块确实在 python 启动的时候被导入了,但是它们不像 __builtins__ 那样直接暴露了出来,它们还隐藏着,对当前作用域是不可见的,需要 import 把它们加入进来后才能使用。所以 Python 模块的加载与否与模块的可见与否是两回事。先来看一个例子:

>>> import json
>>> json
<module 'json' from '/usr/lib/python2.7/json/__init__.pyc'>
>>> del json
>>> del json
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'json' is not defined
>>> josn
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'josn' is not defined
>>> import sys
>>> sys.modules.keys()
['json.decoder', 'encodings.binascii', 'copy_reg', 'sre_compile', '_sre', 'encodings', 'site', '__builtin__', 'sysconfig', '__main__', 'encodings.encodings', 'encodings.hex_codec', 'json.struct', 'abc', 'posixpath', '_weakrefset', 'errno', '_json', 'encodings.codecs', 'sre_constants', 're', 'json', '_abcoll', 'types', '_codecs', 'encodings.__builtin__', '_struct', '_warnings', 'json._json', 'genericpath', 'stat', 'zipimport', '_sysconfigdata', 'warnings', 'UserDict', 'json.json', 'encodings.utf_8', 'json.sys', 'sys', 'json.scanner', 'codecs', 'json.encoder', 'readline', '_sysconfigdata_nd', 'os.path', 'struct', 'json.re', 'sitecustomize', 'signal', 'traceback', 'linecache', 'posix', 'encodings.aliases', 'binascii', 'exceptions', 'sre_parse', 'keyrings', 'os', '_weakref']

由例子可知,可以将导入的模块从当前作用域中删除,但是被导入的模块仍然存在于 sys.modules 中,即使该模块对当前作用域不可见。所以,Python 程序在运行的时候会将所有导入的模块都加载到内存,直到程序结束才被释放。再来另外一个例子:

  • a.py
import sys
import time

__all__ = ["foo"]

sys.test = "This is test in sys."
time.test = "This is test in time."

def foo():
    print "dadada"
  • b.py
from a import *
print dir()
  • testsys.py
import sys
from b import *

print sys.test
print time.test

运行 testsys.py 的输出结果为:

['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'foo']
This is test in sys.
This is test in time.

这里,我们用 b.py 作为一个桥梁将 b.py 导入,在 a.py 中我们用 __all__ 来限制属性的导入,所以在 b.py 中是没有 sys 模块的, print dir() 的输出结果可以证明,但是在 testsys.py 中重新导入 sys 模块,并打印在 a.py 中给 sys 添加的属性 test,这却没有抛出 AttributeError 异常。这就说明 sys 模块一直存在于程序的整个运行过程中。

因此,解释器在启动的时候自动载入一些模块是为了保存某些全局的变量以便于程序使用。