如何在 Python 模块中正确使用相对或绝对导入?

Posted

技术标签:

【中文标题】如何在 Python 模块中正确使用相对或绝对导入?【英文标题】:How to properly use relative or absolute imports in Python modules? 【发布时间】:2011-04-06 17:27:43 【问题描述】:

在 Python 中使用相对导入有一个缺点,您将无法再将模块作为独立模块运行,因为您会遇到异常:ValueError: Attempted relative import in non-package

# /test.py: just a sample file importing foo module
import foo
...

# /foo/foo.py:
from . import bar
...
if __name__ == "__main__":
   pass

# /foo/bar.py: a submodule of foo, used by foo.py
from . import foo
...
if __name__ == "__main__":
   pass

我应该如何修改示例代码以便能够执行所有:test.pyfoo.pybar.py

我正在寻找适用于 python 2.6+(包括 3.x)的解决方案。

【问题讨论】:

查看这个帖子:velocityreviews.com/forums/t502905-relative-import-broken.html 谢谢,不幸的是我知道这个旧线程,但我没有找到解决问题的方法。到目前为止,我只观察到很多人抱怨这个。对于这个问题,我们需要一个明确的解决方案/示例。 相关:How to know if python script was run using interpreter's -m option? 重复How to do relative imports in Python? 注意:投票最多的答案是第二个。 【参考方案1】:

到目前为止,我发现的唯一解决方案是根本不使用相对导入。

由于目前的限制,我想知道什么时候应该有人在 python 中使用相对导入。

在我使用sys.path 的所有配置中,都包含当前目录作为第一个参数,所以只需使用import foo 而不是from . import foo,因为它会做同样的事情。

【讨论】:

但是当您决定要使用与包中的模块同名的python标准或第三方模块时,这会导致问题。这就是为什么恕我直言,最好使用from __future__ import absolute_import 和相关导入。【参考方案2】:

您可以以不同的方式开始“将模块作为独立运行”:

代替:

python foo/bar.py

用途:

python -mfoo.bar

当然,foo/__init__.py 文件必须存在。

还请注意,foo.pybar.py 之间存在循环依赖关系——这不起作用。我想这只是你的例子中的一个错误。

更新:将其用作foo/bar.py 的第一行似乎也很有效:

#!/usr/bin/python -mfoo.bar

然后就可以直接在POSIX系统中执行脚本了。

【讨论】:

这是一个丑陋的 hack,我认为是不可接受的,特别是因为它使执行它们变得更加困难(尤其是在 Windows 上) 顺便说一下,bogdan 的示例有效,即使使用循环导入。 +1,这正是“执行独立”模块的正确方法作为包的一部分(如果你想使用相对导入,你必须这样做,当然)。【参考方案3】:

首先,我假设您意识到您所写的内容会导致循环导入问题,因为 foo 导入 bar,反之亦然;尝试添加

from foo import bar

到 test.py,你会看到它失败了。必须更改示例才能正常工作。

所以,您真正要问的是在相对导入失败时回退到绝对导入;实际上,如果您将 foo.py 或 bar.py 作为主模块执行,则其他模块将仅位于根级别,如果它们与系统上的另一个模块共享名称,则选择哪个模块取决于sys.path 中的顺序。由于当前目录通常是第一个,如果可用,本地模块将被选择 - 即,如果您在当前工作目录中有一个“os.py”文件,它将被选择而不是内置的。

一个可能的建议是:

foo.py

try:
    from . import bar
except ValueError:
    import bar

if __name__ == "__main__":
    pass

bar.py:

if __name__ == "__main__":
    pass

顺便说一句,从正确的位置调用脚本通常方式更好。

python -m foo.bar

可能是最好的方法。这个runs the module as a script。

【讨论】:

我认为except 语句中的import bar 不适用于Python 3.1+ @John 据我所知没有理由,因为在 except 中的导入不应该工作。它在 Python 3.2 中 100% 运行。【参考方案4】:

为什么不把“main”放在不同的 .py 文件中?

【讨论】:

【参考方案5】:

放弃相对导入:无论如何,您应该将包命名空间视为一个全局命名空间。

使这个可口的诀窍是适当地编辑sys.path。这里有一些值得深思的地方:

# 上一个目录 _root_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) sys.path.insert(0, _root_dir) 现在

【讨论】:

-1。不要放弃相对进口;当设计绝对导入功能mail.python.org/pipermail/python-dev/2003-December/041065.html 时,它们被认为很好,今天它们也一样好。【参考方案6】:

每个文件夹中都需要__init__.py

仅当您这样做时,相对导入才有效:

python test.py

test.py 导入 foo.py 和 foo.py 可以相对导入 test.py 及以上文件夹中的任何内容。

你不能这样做:

cd foo
python foo.py
python bar.py

它永远不会起作用。

您可以尝试 sys.path.append 或 sys.path.insert 解决方案,但您会搞砸路径,并且 f=open(filename) 会出现问题。

【讨论】:

以上是关于如何在 Python 模块中正确使用相对或绝对导入?的主要内容,如果未能解决你的问题,请参考以下文章

模块导入循环导入模块查找顺序相对导入及绝对导入

Python 包内的导入问题(绝对导入和相对导入)

详解Python中的相对导入和绝对导入

导入 JavaScript 时如何在 QML 中使用绝对路径?

PYTHON-DAY18-包 相对 绝对导入

如何在python3中正确导入同一目录下的模块