Python文档中有关导入的两部分似乎有些含糊。
导入名为
spam
的模块时,解释器首先搜索具有该名称的内置模块。如果找不到,它将在变量spam.py
给出的目录列表中搜索名为sys.path
的文件。
在导入搜索期间检查的第一位是
sys.modules
。此映射充当所有先前导入的模块(包括中间路径)的缓存。
其中哪些可以更准确地表示Python导入系统内部发生的情况?下面的逻辑将说明它们不能共存,因为sys.modules
可以很好地包含不是内置的模块,并且可以排除某些内置的模块。
这是我困惑的地方:
sys.modules
用于缓存已导入的模块;它不是明确地用于存储内置模块的完整列表。 (我认为最接近的是sys.built_in_modules
,但这还不包括具有.__file__
属性的内容,例如math
。)
如果我启动新的解释器会话,则sys.modules
包含 most 个内置函数,但不包括sys.builtin_module_names
中的某些内容:gc
和{{1 }}等。另外,您可以导入第3方软件包,将其放入time
中,此时sys.modules
当然不再是仅包含内置模块的字典。因此,所有这些似乎都说:“ sys.modules
!=内置模块。”
答案 0 :(得分:1)
导入模块时,解释器首先搜索内置模块,然后搜索sys.path
。但这仅在您确实要导入模块的情况下。 在之前,导入模块需要搜索缓存。如果模块已经在缓存中,则不会再次导入。
答案 1 :(得分:1)
您正在查看两种完全不同的信息源,即教程和语言参考。
教程部分The Module Search Path(仅描述默认行为)也仅描述了实际导入模块时发生的情况。
如果模块已经在缓存中,则不会发生此过程。这里没有解释,因为上一节More on Modules中已经介绍了它:
一个模块可以包含可执行语句以及函数定义。这些语句旨在初始化模块。只有在import语句中第一次遇到模块名称时,它们才会执行。
...
注意出于效率原因,每个模块在每个解释器会话中仅导入一次。
由于这只是一个教程,因此没有说明发生这种情况的机制。
同时,在导入系统的参考文档中,module cache部分说明了import
语句中发生的第一件事。
请注意,如果已导入模块,Python会避免执行模块的语句,或者为提高效率而仅导入一次,这并非完全正确。这是由于默认的加载程序将模块放入sys.modules
缓存中的结果。而且,如果事后替换装载程序或猴子用缓存,则实际上模块将被多次导入并执行。
后续部分(从下一节Finders and loaders开始)比本教程的“模块搜索路径”部分更严格,更详细地描述了如何找到模块的详细信息:
Python包含许多默认查找器和导入器。第一个知道如何找到内置模块,第二个知道如何找到冻结的模块。第三个默认查找器在导入路径中搜索模块。
同样,解释器首先搜索内置模块并非完全正确。相反,解释器仅按顺序搜索其查找器,默认情况下,第一个查找器是内置的模块查找器。但是,如果您更改查找器列表,Python将不会首先搜索内置函数。
实际上,如果在默认安装的CPython 3.7上打印出sys.meta_path
,您将看到:
<class '_frozen_importlib.BuiltinImporter'>
<class '_frozen_importlib.FrozenImporter'>
<class '_frozen_importlib_external.PathFinder'>
(在IPython中,或者如果您导入了有助于重命名模块的six
之类的东西,或者如果您导入了嵌入版本控制的模块之类的requests
之类的东西,额外的发现者。)
importlib
库文档中记录了BuiltinImporter
。 (如果您想知道为什么它不被称为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。
答案 2 :(得分:0)
您需要区分sys.path
和sys.modules
sys.modules 这是将模块名称映射到模块的字典 已经加载的可以操纵以强制 重新加载模块和其他技巧。请注意,卸下模块 从此字典中获取的内容与在上调用reload()不同 相应的模块对象。
当我在jupyter笔记本中加载sys.path
时,显示映射到文件位置的已加载模块名称的字典-
{'IPython': <module 'IPython' from 'C:\\Users\\User\\Anaconda3\\lib\\site-packages\\IPython\\__init__.py'>,
'IPython.core': <module 'IPython.core' from 'C:\\Users\\User\\Anaconda3\\lib\\site-packages\\IPython\\core\\__init__.py'>,.....}
这是我的模块缓存,但是当我尝试
sys.modules['numpy']
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
<ipython-input-6-44b02d746fe5> in <module>()
----> 1 sys.modules['numpy']
KeyError: 'numpy'
因为numpy不在我的模块缓存中。我将要求python在sys.path
中定义的一组固定目录中查找它。我认为合适的字符串列表,可以在其中添加或删除路径。
sys.path (字符串列表),用于指定模块的搜索路径。 从环境变量PYTHONPATH初始化,再加上 取决于安装的默认值。
如果python在我的sys.path
集中找到了该库;它将在我的sys.modules
中为其创建一个映射,以便在活动环境中进行快速访问。
import numpy
sys.modules['numpy']
#<module 'numpy' from 'C:\\Users\\User\\Anaconda3\\lib\\site-packages\\numpy\\__init__.py'>