ChatGPT解决这个技术问题 Extra ChatGPT

ValueError:尝试相对导入超出顶级包

为了更好地理解它的工作原理,我正在玩 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.py,并且 pkg 应该被视为一个包,因为它具有 __init__.py 文件。

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

from .. import d

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


t
typhon04

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

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

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

answer 更深入地解释了 Python 模块的实际工作原理,但要进行总结。

当一个文件被加载时,它被赋予一个名字:如果它被加载为顶级脚本(直接运行),它的名字是__main__。如果它作为一个模块加载(带有导入),它的名称是文件名,前面是它所属的任何包/子包的名称,用点分隔 - pkg.subpkg.a 如果你执行 from ..文件名中必须至少有 2 个点。从 ... - 3 个点。

现在是有趣的部分。

如果您直接运行 c.py,则它的名称为 __main__,而 a.py 具有 subpkg.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

d
dfrankow

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

这里正确的用法应该是

from .subpkg import a

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

尝试:

sys.path.insert(0, '')

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

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

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


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

我找到了这个解决方案:

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

这不会使您的代码依赖于进程的工作目录吗?如果我从其他地方运行它或 chdir 到不同的目录,它会失败,我相信。
A
Anil_rockstar

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

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

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

从文件夹 2.file1 导入 <方法名>


A
Anaël Leinert

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


M
Mark Z.

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


关注公众号,不定期副业成功案例分享
关注公众号

不定期副业成功案例分享

领先一步获取最新的外包任务吗?

立即订阅