urllib3 中的相对导入

Relative import in urllib3

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

我从 requests 开始加载 urllib3

我找到了 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.sixurllib3.packages 不会加载该文件。

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,导入它,before 它'我会寻找更多的名字。

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

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

A direct call to __import__() performs only the module search and, if found, the module creation operation. While certain side-effects may occur, such as the importing of parent packages, and the updating of various caches (including sys.modules), only the import statement performs a name binding operation.

Regular packages

以下

Importing parent.one will implicitly execute parent/__init__.py and parent/one/__init__.py. Subsequent imports of parent.two or parent.three will execute parent/two/__init__.py and parent/three/__init__.py respectively.

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. So if foo.bar.baz was previously imported, sys.modules will contain entries for foo, foo.bar, and foo.bar.baz. Each key will have as its value the corresponding module object.

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

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

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

Python 中关于导入函数的一个值得注意的事情是,它不仅可以导入模块本身,还可以从其中导入变量、classes、函数(通常是名称空间)。 在示例中,您提供了:

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

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

moves = _MovedItems(__name__ + ".moves")

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