Python 导入搜索路径 : what happens first?

标签 python python-3.x python-import

Python 文档的两部分关于导入的措辞似乎有些模棱两可。

来自 "The Module Search Path" :

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 named spam.py in a list of directories given by the variable sys.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 中排除一些东西:即、gctime 等。此外,您可以导入第三方包,这些包将被放入 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/

相关文章:

python-import - Python 中的合格导入

python - 从 Postgres 数据库获取数据时内存使用过多

python-3.x - 在 python 中处理两个不同大小的数据框

python - 列表附加在循环问题中

python-3.x - 使用boto3创建RDS实例时指定VPC

python - 在python中将另一个项目作为模块导入

Python 内部结构 - 对象如何了解全局变量?

python - 在Windows中使用python获取重启历史记录

python - 使用平均阈值过滤数据帧

python - Python 中的高效排类