代码之家  ›  专栏  ›  技术社区  ›  Brad Solomon

python导入搜索路径:首先发生什么?

  •  2
  • Brad Solomon  · 技术社区  · 6 年前

    python文档中关于导入的两部分似乎有些含糊不清。

    "The Module Search Path" 以下内容:

    当一个模块 spam 导入后,解释器首先搜索具有该名称的内置模块。如果未找到,则搜索名为 spam.py 在变量给出的目录列表中 sys.path 是的。

    "The Module Cache" 以下内容:

    在导入搜索期间检查的第一个位置是 sys.modules 是的。此映射用作先前导入的所有模块的缓存,包括中间路径。

    以上哪一个更准确地描述了Python导入系统内部发生的事情?下面的逻辑会说他们不能共存,因为 系统模块 很可能包含不是内置的模块,也可能排除一些是内置的模块。


    我的困惑源于:

    系统模块 是用于缓存已导入的模块;不是专门用于存储内置模块的完整列表。(我想,最接近的是 sys.built_in_modules ,但也不包括 .__file__ 属性,例如 math (第三章)

    如果我启动一个新的口译员会话, 系统模块 包含 内置,但不包括 sys.builtin_module_names :即, gc time 等等。此外,您还可以导入第三方软件包,这些软件包将放入 系统模块 ,此时 系统模块 当然不再是只包含内置模块的字典。所以,所有这些似乎都表明, 系统模块 啊!=内置模块。”

    3 回复  |  直到 6 年前
        1
  •  1
  •   Brad Solomon    5 年前

    您将看到两个完全不同的信息源,教程和语言参考。


    教程部分 The Module Search Path (除了只描述默认行为外)还只描述了实际导入模块时发生的情况。

    如果模块已经在缓存中,则不会发生此过程。这里没有解释,因为上一节已经讨论过了, More on Modules 以下内容:

    模块可以包含可执行语句和函数定义。这些语句旨在初始化模块。它们只在import语句中第一次遇到模块名时执行。

    注意:为了提高效率,每个模块在每个解释器会话中只导入一次。

    它没有解释发生这种情况的机制,因为这只是一个教程。


    同时,在导入系统的参考文档中, module cache 部分解释了 import 陈述。

    注意,如果模块已经被导入,python会避免执行模块的语句,或者为了提高效率只导入一次,这并不完全正确。这是默认加载程序将模块放在 sys.modules 隐藏物。如果您在事实发生后用缓存替换加载程序或monkey,那么实际上一个模块将被多次导入和执行。

    下一节从下一节开始, Finders and loaders 与本教程的“模块搜索路径”部分相比,类似地描述了如何找到模块的详细信息:

    python包含许多默认的查找程序和导入程序。第一个知道如何定位内置模块,第二个知道如何定位冻结模块。第三默认查找器搜索模块的导入路径。

    所以再说一遍,不是 确切地 确实,解释器首先搜索内置模块。相反,解释器只是按顺序搜索它的查找器,默认情况下,第一个查找器是内置的模块查找器。但如果更改查找器列表,python将不会首先搜索内置项。


    事实上,如果你打印出来 sys.meta_path 在cpython 3.7的默认安装中,您将看到:

    <class '_frozen_importlib.BuiltinImporter'>
    <class '_frozen_importlib.FrozenImporter'>
    <class '_frozen_importlib_external.PathFinder'>
    

    (在ipython下,或者如果您导入了 six 这有助于重命名模块,或者如果您导入了 requests 嵌入有版本控制的模块后,您将有几个额外的查找程序。)

    那个 BuiltinImporter 记录在 importlib 图书馆文献。(如果你想知道为什么不叫它 BuiltinFinder ,同时也是其自身加载程序的finder称为importer)。 sys.builtin_module_names 调用特定于实现的函数来处理在那里找到的任何东西。


    In CPython 3.6 (道歉在3.6到3.7之间来回跳动,但在这里不重要),它所调用的实现特定的功能是 _imp.create_builtin ,你可以从那里追踪到东西。

    但需要注意的是 builtin_module_names 实际上是“内置”的,因为它是预先导入的。例如,在正常安装时,您可能会看到 _ast 在那里,但是没有 sys.modules['_ast'] 是的。

    所以 create_builtin 函数(或者,对于不同的实现,无论它使用什么来实现 室内搬运工 )必须能够导入预安装在python中的so/dll/pyd/dylib模块。

        2
  •  1
  •   nosklo    6 年前

    当你这样做的时候 进口 一个模块,解释器首先搜索内置模块,然后 sys.path .但只有当你真的要导入这个模块的时候。 以前 导入模块时,有一个要搜索的缓存。如果模块已在缓存中,则不会再次导入。

        3
  •  0
  •   pyeR_biz    6 年前

    你需要区分 sys.path sys.modules

    系统模块 这是一个将模块名映射到模块的字典 已经装好了。这可以用来强迫 重新加载模块和其他技巧。请注意,卸下模块 从这个字典中调用reload()与在 对应的模块对象。

    当我装载 搜索路径 在jupyter笔记本中,显示映射到文件位置的已加载模块名称字典-

    {'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在一组固定的目录中查找它,这些目录在 搜索路径 是的。一个字符串列表,我可以在其中添加或删除我认为合适的路径。

    搜索路径 指定模块搜索路径的字符串列表。 从环境变量Python Plus初始化,再加上 依赖于安装的默认值。

    如果python在我的 搜索路径 它将在我的 系统模块 以便在活动环境中快速访问。

    import numpy
    sys.modules['numpy']
    #<module 'numpy' from 'C:\\Users\\User\\Anaconda3\\lib\\site-packages\\numpy\\__init__.py'>