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

问题描述 投票:0回答:6

我在玩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

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

python-3.x python-import
6个回答
51
投票

这让我质疑自己的疯狂。

问题源于人们的困惑错误地将相对导入视为路径相对,而事实并非如此。

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

这个

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

    加载文件时,会为其指定一个名称:
    • 如果是作为顶层脚本加载
    • (直接运行),它的名字是__main__
    • 如果它作为模块加载
    • (带有导入),它的名称是文件名,前面是它所属的任何包/子包的名称,用点分隔 - pkg.subpkg.a
      
      
  1. 如果您执行
  2. from ..
    ,文件名中必须至少有 2 个点。 
    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
    

5
投票
Python 3 更改了导入系统,因此每次您想要一个与您正在使用的模块相关的模块时,您都需要相对导入(除非您搞乱了

PYTHONPATH

sys.path
)。

这里正确的用法应该是

from .subpkg import a

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

尝试:

sys.path.insert(0, '')

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

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

尝试这个答案:

https://stackoverflow.com/a/17361545/754991


4
投票
我找到了这个解决方案:

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

2
投票
只需在所有文件夹中添加/创建

__init__.py

 文件即可解决问题。

    文件夹1
    • 文件夹2
      • 文件1.py
    • 文件夹3
      • 文件夹4
        • 文件2.py
我想使用

file1.py

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

from folder2.file1 import <method_name>
    

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


0
投票
我很感谢最重要的答案,但我发现建议的修复有点不令人满意。这是我的建议:只需将 sys.path.append(".") 添加到您的主文件中。这允许导入第一级包而不需要对项目架构进行任何更改。只需删除之前的 .. 不再需要的即可。

© www.soinside.com 2019 - 2024. All rights reserved.