ValueError:尝试相对导入超出***包

Posted

技术标签:

【中文标题】ValueError:尝试相对导入超出***包【英文标题】:ValueError: attempted relative import beyond top-level package 【发布时间】:2016-05-12 01:06:35 【问题描述】:

为了更好地理解它的工作原理,我正在玩 Python 的导入系统,但遇到了另一个问题。我有以下结构

pkg/
    __init__.py
    c.py
    d.py

    subpkg/
        __init__.py
        a.py
        b.py

a.py里面我有如下代码:

from . import b
from .. import d

c.py 里面我有以下内容:

import subpkg.a

现在我收到以下错误:

ValueError: 尝试相对导入超出***包

但是为什么?我该如何解决?我从 IDLE 运行 c.pypkg 应该被视为一个包,因为它有 __init__.py 文件。

第一次导入工作正常,但以下不起作用:

from .. import d

因为我试图从父包中导入一些东西,但显然我不能,出于某种奇怪的原因。

【问题讨论】:

【参考方案1】:

typhon04 有一个很好的描述,帮助我理解了这个问题,但我不同意他在一切之外创建一个虚拟 main.py 的结论。鉴于我们与 c.py 相关,答案似乎是我们不再需要“from ..”,而只需“import d”就足够了。

【讨论】:

【参考方案2】:

只需在所有文件夹中添加/创建 init.py 文件即可解决问题。

文件夹 1 -文件夹2 -file1.py -文件夹3 -文件夹4 - 文件2.py

我想在 file2.py 中使用 file1.py 中存在的一种方法 [基本上向上两层]。 所以我在上面的所有文件夹和子文件夹中添加了空的 init.py 并在下面的 file2.py 中使用:-

从文件夹 2.file1 导入

【讨论】:

【参考方案3】:

我很感谢最佳答案,但我发现建议的修复有点不满意。这是我的建议:只需将 sys.path.append(".") 添加到您的主文件中。这允许在不更改项目架构的情况下导入第一级包。只需删除以前的 .. 不再需要。

【讨论】:

【参考方案4】:

我找到了这个解决方案:

#! /usr/bin/env python
import os
import sys
sys.path.append(os.path.realpath('.'))
from d import *

【讨论】:

这不是让你的代码依赖于进程的工作目录吗?如果我从其他地方运行它或将chdir 运行到不同的目录,我相信它会失败。【参考方案5】:

这让我怀疑自己的精神错乱。

问题源于人们错误地将相对导入作为路径相对而不是的混淆。

相对导入取决于运行文件的位置。

answer 更深入地解释了 python 模块的实际工作原理,但要总结一下。

    加载文件时,会为其命名: 如果作为顶层脚本加载(直接运行),它的名字是__main__。 如果它是作为模块加载的(带导入),它的名称是文件名,前面是它所属的任何包/子包的名称,用点分隔 - @987654323 @ 如果您执行from ..,文件名中必须至少有两个点。 from ... - 3 个点。

现在有趣的部分来了。

如果你直接运行c.py,那么它的名字是__main__,而a.pysubpkg.a

根据第二条语句,subpkg.a 的名称中必须至少有 2 个点才能在其中运行 from ..

修复

pkg之外创建一个新文件,比如main.py

pkg/
    __init__.py
    c.py
    d.py

    subpkg/
        __init__.py
        a.py
        b.py
main.py

main.py 内部

import pkg.c

如果我们运行 main.py,它会得到名称 __main__,而 a.py 会得到 pkg.subpkg.a。根据第二条语句,现在名称中有 2 个点,我们可以使用 from ..

还有一件事。现在 c.py 作为模块加载了,我们必须使用 from 来加载 a.py

from .subpkg import a

【讨论】:

【参考方案6】:

Python 3 更改了导入系统,因此每次您想要一个与您正在工作的模块相关的模块时,您都需要相对导入(除非您弄乱了 PYTHONPATHsys.path)。

这里正确的用法应该是

from .subpkg import a

当您使用 IDLE 时,您将拥有一个完全不同的环境。因此,您可以将当前位置添加到您的路径中,以便再次导入。

尝试:

sys.path.insert(0, '')

这可能很奇怪,但这是为了更大的利益

PS:如果最后这件事不起作用——我现在没有空闲环境——可能是因为工作目录设置错误。

试试这个答案:https://***.com/a/17361545/754991

【讨论】:

如果我这样做from .subpkg import a,我会从 IDLE 和终端收到:SystemError: Parent module '' not loaded, cannot perform relative import... @nbro 那是因为您在 IDLE 中,但是当您在模块中运行它时,导入应该是这种方式。 IDLE 是一个糟糕的 IDE,通常不利于 Python 开发。在 IDLE 和内部模块上最有效的选项是使用完整的包名称:from pkg import subpkg.asys.path 上添加 pkg 的目录之后 你是什么意思:“当你在一个模块中运行它时”?我说,我按照您的建议从终端和 IDLE 运行文件c.py(作为主文件),它给了我上面提到的错误。 IDLE 是一个糟糕的 IDE,但它带有 Python?携带一个糟糕的 IDE 的目的是什么?你可以说 IDLE 是一个简单的 IDE,它肯定不是最好的,但理论上它应该可以很好地与 Python 配合使用,否则没有任何意义。 我对你所说的和 Python 的导入系统大体上玩了一点,我想我更了解它是如何工作的。基本上,作为“主”运行的文件和作为由其他模块或“主”文件导入的模块或包之间存在区别。如果您只有一个运行整个应用程序的文件,您可以在子模块和子包之间进行相对导入。另一方面,正如您所说,相对导入在作为“主”运行的文件中不起作用。不幸的是,这是一个糟糕的限制...... from .subpkg import a 没有回答这个问题。问题是问如何from .. import d

以上是关于ValueError:尝试相对导入超出***包的主要内容,如果未能解决你的问题,请参考以下文章

尝试相对导入超出***包 python

尝试使用 boost/Python 相对导入超出***包

ValueError:尝试在非包中进行相对导入[重复]

ValueError:尝试在 pyspark 中的非包中进行相对导入/无法找到 kmodes 模块(pyspark)

Python 3模块从另一个文件夹导入错误[重复]

Python包的相对导入时出现问题解决