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.py
,pkg
应该被视为一个包,因为它有 __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.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
【讨论】:
【参考方案6】:Python 3 更改了导入系统,因此每次您想要一个与您正在工作的模块相关的模块时,您都需要相对导入(除非您弄乱了 PYTHONPATH
或 sys.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.a
在 sys.path
上添加 pkg
的目录之后
你是什么意思:“当你在一个模块中运行它时”?我说,我按照您的建议从终端和 IDLE 运行文件c.py
(作为主文件),它给了我上面提到的错误。 IDLE 是一个糟糕的 IDE,但它带有 Python?携带一个糟糕的 IDE 的目的是什么?你可以说 IDLE 是一个简单的 IDE,它肯定不是最好的,但理论上它应该可以很好地与 Python 配合使用,否则没有任何意义。
我对你所说的和 Python 的导入系统大体上玩了一点,我想我更了解它是如何工作的。基本上,作为“主”运行的文件和作为由其他模块或“主”文件导入的模块或包之间存在区别。如果您只有一个运行整个应用程序的文件,您可以在子模块和子包之间进行相对导入。另一方面,正如您所说,相对导入在作为“主”运行的文件中不起作用。不幸的是,这是一个糟糕的限制......
from .subpkg import a
没有回答这个问题。问题是问如何from .. import d
。以上是关于ValueError:尝试相对导入超出***包的主要内容,如果未能解决你的问题,请参考以下文章