【问题标题】:Relative import in urllib3urllib3 中的相对导入
【发布时间】:2019-03-23 17:23:07
【问题描述】:

我正在研究python如何加载模块。

我从加载urllib3requests开始。

我找到了those line:

from .packages.six.moves.http_client import (
  IncompleteRead as httplib_IncompleteRead
)

在名为src/urllib3/exceptions.py的文件中

那个导入是一个相对的,所以我正在寻找文件夹src/urllib3/packages/six/moves/http_client.pysrc/urllib3/packages/six/moves/http_client/__init__.py

那些文件不存在。

谢天谢地 src/urllib3/packages/six.py 定义了一个新的模块加载器,因此导入 .packages.six.moves.http_client 需要 http_client 核心 python 模块。

但我不明白src/urllib3/packages/six.py 是如何加载的。 我没有找到那种导入。

有没有办法知道哪个模块直接加载src/urllib3/packages/six.py

我已按照建议 here 尝试使用 __name__ 全局变量。它的值为urllib3.packages.six,但urllib3.packages 不会加载该文件。

【问题讨论】:

    标签: python python-import


    【解决方案1】:

    from .packages.six.moves.http_client import ... 表达式导致 .packages.six 被加载首先。 Python 总是将所有包加载到一个模块的嵌套包引用中。

    所以.packages.six.moves.http_client 导致Python 首先查找urllib3.packages,然后查找urllib3.packages.six,依此类推。导入机制通过在 sys.modules 中查找全名来完成此操作,如果不存在,则触发模块搜索并为每个模块加载。

    第一次发生这种情况时,sys.modules['urllib3.packages.six'] 尚不存在,导入机制找到文件 urllib3/packages/six.py,导入该文件,在此之前它会查找更多名称。

    而且,正如您所发现的,导入 six.py 模块文件的行为会导致该模块添加 sys.modules['urllib3.packages.six.moves'] 并进一步引用标准库模块。

    Python 的导入机制相当复杂; Python 参考文档在The import system 中全面介绍了这一点;要查找的具体条目是:

    直接调用__import__() 只执行模块搜索,如果找到,则执行模块创建操作。虽然可能会出现某些副作用,例如导入父包,以及更新各种缓存(包括 sys.modules),但只有 import 语句执行名称绑定操作。

    及以下Regular packages

    导入parent.one 将隐式执行parent/__init__.pyparent/one/__init__.py. parent.twoparent.three 的后续导入将分别执行parent/two/__init__.pyparent/three/__init__.py

    The module cache以下:

    导入搜索期间检查的第一个位置是sys.modules。此映射用作之前已导入的所有模块的缓存,包括中间路径。因此,如果之前导入了 foo.bar.bazsys.modules 将包含 foofoo.barfoo.bar.baz 的条目。每个键都有对应的模块对象作为其值。

    (我添加的引用部分中的粗体强调)。

    注意urllib3/packages 目录中的所有内容都是一个供应商 包;一个通常会独立安装的项目,但urllib3 项目已决定将其与自己的发行版打包在一起,以避免担心要支持哪些版本。 six就是这样一个独立的项目,你可以安装它from PyPI

    您可以在six project documentation 中找到有关six.moves virtual package 的更多信息。其目的是让库开发人员更轻松地编写与 Python 2 和 Python 3 兼容的代码,而不必担心要在任一版本上导入什么标准库名称。

    【讨论】:

      【解决方案2】:

      Python 中的 import 函数值得注意的一点是,它不仅可以导入模块本身,还可以从它们内部导入变量、类、函数(通常是命名空间)。 在示例中,您提供了:

      from .packages.six.moves.http_client import (
        IncompleteRead as httplib_IncompleteRead
      )
      

      import 语句引用来自 src/urllib3/packages/six.py 模块的 moves 变量,该变量在该文件的第 316 行定义并分配给类 _MovedItems 的实例:

      moves = _MovedItems(__name__ + ".moves")
      

      http_client是这个类实例的属性或方法

      【讨论】:

      • 最值得注意的是from ... import ...语句中的之前部分import必须都是模块,这些名称将在sys.modules中查找并且没有属性会被查询。所以对于这种特定情况urllib3.packages.sixsys.modules['urllib3.packages.six.moves'] 及以后安装虚拟模块,你可以从最终模块对象导入属性一点都没有关系。
      • 哇,我不知道。你成功了
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-05-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-21
      • 1970-01-01
      • 2013-06-03
      相关资源
      最近更新 更多