Python 中的隐式相对导入如何工作?

How do implicit relative imports work in Python?

假设我有以下文件,

pkg/
pkg/__init__.py
pkg/main.py # import string
pkg/string.py # print("Package's string module imported")

现在,如果我 运行 main.py,它会显示 "Package's string module imported"

这是有道理的,并且按照 link 中的声明工作:

"it will first look in the package's directory"

假设我稍微修改了文件结构(添加了一个核心目录):

pkg/
pkg/__init__.py
plg/core/__init__.py
pkg/core/main.py # import string
pkg/string.py # print("Package's string module imported")

现在,如果我 运行 python core/main.py,它会加载 built-in string 模块。

同样在第二种情况下,如果它必须遵守声明“它将首先在包的目录中查找” 它不应该加载本地 string.py 因为 pkg 是 "package directory"?

我对术语 "package directory" 的理解是特别是 [=59] 的根文件夹 =] 个文件夹 __init__.py。所以在这种情况下,pkg 就是 "package directory"。它适用于 main.py 以及 core/main.py 等子目录中的文件,因为它是 "package".

的一部分

这在技术上是否正确?

PS:代码段中#之后的内容是文件的实际内容(没有前导空格)。

包是带有 __init__.py 文件的目录,是的,当在模块搜索路径 上找到时,它们会作为模块 加载。所以 pkg 只是一个可以导入的包,如果父目录在模块搜索路径上.

,则可以将其视为包

但是通过运行将pkg/core/main.py文件作为脚本,Python将pkg/core目录添加到模块搜索路径,而不是 pkg 的父目录。现在您的模块搜索路径上确实有一个 __init__.py 文件,但这不是定义包的内容。你只有一个 __main__ 模块,与其他任何东西都没有包关系,你不能依赖隐式相对导入。

您有三个选择:

  1. 不要 运行 包内的文件作为脚本。将脚本文件 放在包的 之外,并根据需要导入您的包。您可以将它 next 放到 pkg 目录中,或者确保 pkg 目录首先安装到模块搜索路径中已有的目录中,或者通过让您的脚本计算出添加到 sys.path.

  2. 的正确路径
  3. 使用-m command line switch到运行一个模块就好像它是一个脚本。如果您使用 python -m pkg.core Python 将查找 __main__.py 文件和 运行 作为脚本。 -m 开关会将当前工作目录添加到您的模块搜索路径中,因此您可以在正确的工作目录中使用该命令,一切都会正常进行。或者将您的包安装在模块搜索路径中已有的目录中。

  4. 让您的脚本将正确的目录添加到模块搜索路径(基于 os.path.absolute(__file__) 获取当前文件的路径)。考虑到您的脚本 always 名为 __main__,导入 pkg.core.main 将添加第二个独立的模块对象;你会有两个独立的命名空间。

我还强烈建议不要使用隐式相对导入。您可以通过添加同名的嵌套包或模块来轻松屏蔽顶级模块和包。如果您尝试在 pkg 包中使用 import time,则 pkg/time.py 会在标准库 time 模块之前找到。相反,使用 显式 相关模块引用的 Python 3 模型;将 from __future__ import absolute_import 添加到您的所有文件,然后使用 from . import <name> 明确说明您的模块是从哪里导入的。