Python 文档的两部分关于导入的措辞似乎有些模棱两可。
When a module named
spam
is imported, the interpreter first searches for a built-in module with that name. If not found, it then searches for a file namedspam.py
in a list of directories given by the variablesys.path
.
来自 "The Module Cache" :
The first place checked during import search is
sys.modules
. This mapping serves as a cache of all modules that have been previously imported, including the intermediate paths.
以下哪一项更准确地表示了 Python 导入系统内部发生的情况?下面的逻辑会说它们不能共存,因为 sys.modules
很可能包含非内置模块,并且可能排除一些内置模块。
这是我的困惑的来源:
sys.modules
用于缓存已经导入的模块;它并不是专门用于存储内置模块的完整列表。 (我认为最接近的是 sys.built_in_modules
,但它也不包括具有 .__file__
属性的内容,例如 math
.)
如果我启动一个新的解释器 session ,sys.modules
包含大多数 内置函数,但从 sys.builtin_module_names
中排除一些东西:即、gc
和 time
等。此外,您可以导入第三方包,这些包将被放入 sys.modules
中,此时 sys.modules
不再是仅包含内置的字典- 在模块中。所以,所有这些似乎都在说,“sys.modules
!= 内置模块。”
最佳答案
您正在查看两个完全不同的信息来源,教程和语言引用。
教程部分The Module Search Path (除了仅描述默认行为之外)还仅描述了实际导入模块时发生的情况。
如果模块已经在缓存中,这个过程就不会发生。这里不解释,因为它已经在上一节中介绍过,More on Modules :
A module can contain executable statements as well as function definitions. These statements are intended to initialize the module. They are executed only the first time the module name is encountered in an import statement.
...
Note For efficiency reasons, each module is only imported once per interpreter session.
它没有解释发生这种情况的机制,因为这只是一个教程。
同时,在导入系统的引用文档中,module cache部分解释了 import
语句中发生的第一件事。
请注意,如果模块已经被导入,Python 会避免执行模块的语句,或者为了效率只导入一次,这并不完全正确。这是默认加载器将模块放入 sys.modules
缓存这一事实的结果。如果您替换加载器,或者在事后使用缓存进行修改,一个模块实际上将被导入并执行多次。
后续部分——从下一节开始,Finders and loaders —类似地描述了如何找到模块的细节,比教程的模块搜索路径部分更严格、更详细:
Python includes a number of default finders and importers. The first one knows how to locate built-in modules, and the second knows how to locate frozen modules. A third default finder searches an import path for modules.
因此,解释器首先搜索内置模块并不完全正确。相反,解释器只是按顺序搜索它的查找器,默认情况下,第一个查找器是内置模块查找器。但是,如果您更改查找器列表,Python 将不会首先搜索内置函数。
事实上,如果您在默认安装的 CPython 3.7 上打印出 sys.meta_path
,您将看到:
<class '_frozen_importlib.BuiltinImporter'>
<class '_frozen_importlib.FrozenImporter'>
<class '_frozen_importlib_external.PathFinder'>
(在 IPython 下,或者如果你已经导入了类似 six
的东西来帮助重命名模块,或者如果你已经导入了类似 requests
的东西来嵌入版本化模块,你会有几个额外的发现者。)
那个BuiltinImporter
记录在 importlib
库文档中。 (如果你想知道为什么它不被称为 BuiltinFinder
,一个同时也是它自己的加载器的查找器被称为导入器。)它实际上所做的是查看 sys.builtin_module_names
。并调用特定于实现的函数来处理在那里找到的任何内容。
In CPython 3.6 (很抱歉在 3.6 和 3.7 之间来回跳转,但这应该无关紧要……),它调用的特定于实现的函数是 _imp.create_builtin
,您可以从那里追踪事物。
但需要注意的关键是,并非 builtin_module_names
中的所有内容实际上都是“内置”的,因为它是预先导入的。例如,对于正常安装,您可能会在那里看到 _ast
,但看不到 sys.modules['_ast']
。
所以 create_builtin
函数(或者,对于不同的实现,无论它用来实现 BuiltinImporter
)必须能够导入 so/dll/pyd/dylib预装 Python 的模块。
关于Python 导入搜索路径 : what happens first?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51030395/