798

我正在尝试遵循PEP 328,具有以下目录结构:

pkg/
  __init__.py
  components/
    core.py
    __init__.py
  tests/
    core_test.py
    __init__.py

core_test.py我有以下导入语句

from ..components.core import GameLoopEvents

但是,当我运行时,我收到以下错误:

tests$ python core_test.py 
Traceback (most recent call last):
  File "core_test.py", line 3, in <module>
    from ..components.core import GameLoopEvents
ValueError: Attempted relative import in non-package

环顾四周,我发现“即使使用 __init__.py 相对路径也不起作用”和“从相对路径导入模块”,但它们没有帮助。

我在这里有什么遗漏吗?

4

20 回答 20

672

详细说明Ignacio Vazquez-Abrams 的回答:

Python 导入机制相对于__name__当前文件起作用。当你直接执行一个文件时,它没有它通常的名字,而是有"__main__"它的名字。所以相对进口不起作用。

正如 Igancio 建议的那样,您可以使用该-m选项执行它。如果您的包的一部分打算作为脚本运行,您还可以使用该__package__属性来告诉该文件它应该在包层次结构中具有什么名称。

有关详细信息,请参阅http://www.python.org/dev/peps/pep-0366/

于 2012-07-18T08:26:51.360 回答
466

是的。你没有把它当作一个包来使用。

python -m pkg.tests.core_test
于 2012-07-18T08:01:04.023 回答
214

这取决于您要如何启动脚本。

如果您想以经典方式从命令行启动 UnitTest,即:

python tests/core_test.py

然后,由于在这种情况下'components''tests'是同级文件夹,您可以使用sys.path模块的insertappend方法导入相关模块。就像是:

import sys
from os import path
sys.path.append( path.dirname( path.dirname( path.abspath(__file__) ) ) )
from components.core import GameLoopEvents

否则,您可以使用“-m”参数启动脚本(请注意,在这种情况下,我们谈论的是一个包,因此您不能提供“.py”扩展名),即:

python -m pkg.tests.core_test

在这种情况下,您可以像以前一样简单地使用相对导入:

from ..components.core import GameLoopEvents

您最终可以混合使用这两种方法,以便您的脚本无论如何调用都能正常工作。例如:

if __name__ == '__main__':
    if __package__ is None:
        import sys
        from os import path
        sys.path.append( path.dirname( path.dirname( path.abspath(__file__) ) ) )
        from components.core import GameLoopEvents
    else:
        from ..components.core import GameLoopEvents
于 2015-01-10T13:36:37.273 回答
210

import components.core如果将当前目录附加到,则可以直接使用sys.path

if __name__ == '__main__' and __package__ is None:
    from os import sys, path
    sys.path.append(path.dirname(path.dirname(path.abspath(__file__))))
于 2013-10-04T21:00:02.010 回答
31

在 core_test.py 中,执行以下操作:

import sys
sys.path.append('../components')
from core import GameLoopEvents
于 2017-01-10T17:44:57.223 回答
11

如果您的用例用于运行测试,并且确实如此,那么您可以执行以下操作。而不是像运行测试脚本那样python core_test.py使用测试框架,例如pytest. 然后可以在命令行输入

$$ py.test

这将在您的目录中运行测试。这解决了__name__@BrenBarn__main__指出的存在问题。接下来,将一个空__init__.py文件放入您的测试目录,这将使测试目录成为您包的一部分。然后你就可以做

from ..components.core import GameLoopEvents

但是,如果您将测试脚本作为主程序运行,那么事情将再次失败。所以只需使用测试运行器。也许这也适用于其他测试运行器,例如nosetests但我还没有检查过。希望这可以帮助。

于 2015-11-13T17:00:09.563 回答
10

我的快速修复是将目录添加到路径:

import sys
sys.path.insert(0, '../components/')
于 2016-11-01T07:19:41.110 回答
10

问题在于您的测试方法,

你试过python core_test.py

那么你会得到这个错误 ValueError: Attempted relative import in non-package

原因:您正在从非包源测试您的包。

所以从包源测试你的模块。

如果这是您的项目结构,

pkg/
  __init__.py
  components/
    core.py
    __init__.py
  tests/
    core_test.py
    __init__.py

光盘包装

python -m tests.core_test # dont use .py

或从外部 pkg/

python -m pkg.tests.core_test

.如果要从同一目录中的文件夹导入,则为单个。每后退一步再加一个。

hi/
  hello.py
how.py

how.py

from .hi import hello

如果你想从 hello.py 导入如何

from .. import how
于 2019-11-19T08:35:52.963 回答
4

正如Paolo所说,我们有 2 种调用方法:

1) python -m tests.core_test
2) python tests/core_test.py

它们之间的一个区别是 sys.path[0] 字符串。由于在执行 import 时解释器会搜索 sys.path,我们可以这样做tests/core_test.py

if __name__ == '__main__':
    import sys
    from pathlib import Path
    sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
    from components import core
    <other stuff>

在此之后,我们可以使用其他方法运行 core_test.py:

cd tests
python core_test.py
python -m core_test
...

注意,py36 仅测试过。

于 2018-09-20T08:01:03.590 回答
3

旧线程。我发现__all__= ['submodule', ...]__init__.py文件中添加一个,然后from <CURRENT_MODULE> import *在目标中使用它可以正常工作。

于 2018-04-18T17:13:32.710 回答
3

你可以使用from pkg.components.core import GameLoopEvents,例如我使用pycharm,下面是我的项目结构图,我只是从根包中导入,然后就可以了:

在此处输入图像描述

于 2018-07-14T11:20:11.653 回答
3

这种方法对我有用,并且比某些解决方案更简洁:

try:
  from ..components.core import GameLoopEvents
except ValueError:
  from components.core import GameLoopEvents

父目录在我的PYTHONPATH中,父目录和这个目录下都有__init__.py文件。

以上在 python 2 中始终有效,但 python 3 有时会遇到 ImportError 或 ModuleNotFoundError (后者是 python 3.6 中的新内容,是 ImportError 的子类),因此以下调整在 python 2 和 3 中都适用于我:

try:
  from ..components.core import GameLoopEvents
except ( ValueError, ImportError):
  from components.core import GameLoopEvents
于 2019-05-13T22:36:27.693 回答
2

试试这个

import components
from components import *
于 2018-05-24T05:41:17.527 回答
1

如果有人正在寻找解决方法,我偶然发现了一个。这里有一点上下文。我想测试我在文件中的一种方法。当我从内部运行它时

if __name__ == "__main__":

它总是抱怨相对进口。我尝试应用上述解决方案,但未能奏效,因为有许多嵌套文件,每个文件都有多个导入。

这就是我所做的。我刚刚创建了一个启动器,一个可以导入必要方法并调用它们的外部程序。虽然,这不是一个很好的解决方案,但它确实有效。

于 2018-02-21T10:17:58.670 回答
1

这是一种会激怒所有人但效果很好的方法。在测试运行中:

ln -s ../components components

然后像往常一样导入组件。

于 2019-12-02T22:54:48.100 回答
1

由于您已经将所有内容标记为模块,因此如果您作为 python 模块启动,则无需使用相对引用。

代替

from ..components.core import GameLoopEvents

简单地

from pkg.components.core import GameLoopEvents

当您从 pkg 的父级运行时,请使用以下命令

python -m pkg.tests.core_test
于 2020-06-13T20:27:33.567 回答
1

对我来说只有这个工作:我必须明确地将package的值设置为父目录,并将父目录添加到 sys.path

from os import path
import sys
if __package__ is None:
    sys.path.append( path.dirname( path.dirname( path.abspath(__file__) ) ) )
    __package__= "myparent"

from .subdir import something # the . can now be resolved

我现在可以直接使用python myscript.py.

于 2020-10-27T23:12:15.647 回答
0

这非常令人困惑,如果您使用的是 pycharm 之类的 IDE,那就更令人困惑了。什么对我有用: 1. 进行 pycharm 项目设置(如果你从 VE 或 python 目录运行 python) 2. 你定义的方式没有错。有时它适用于 from folder1.file1 导入类

如果它不起作用,请使用 import folder1.file1 3. 您的环境变量应在系统中正确提及或在命令行参数中提供。

于 2020-02-11T09:26:00.510 回答
0

python <main module>.py不适用于相对导入

问题是当您从命令行运行模块时,相对导入不起作用__main__

python <main_module>.py

PEP 338中明确说明了这一点。

2.5b1 的发布显示了此 PEP 和 PEP 328 之间令人惊讶的(尽管回想起来很明显)交互 -显式相对导入在主模块中不起作用。这是因为相对导入依赖于__name__确定当前模块在包层次结构中的位置。在主模块中,值__name__always '__main__',因此显式相对导入总是会失败(因为它们仅适用于包内的模块)。

原因

这个问题实际上并不是 -m 开关所独有的。问题是相对导入基于__name__,并且在主模块中,__name__始终具有值__main__。因此,相对导入目前无法从应用程序的主模块正常工作,因为主模块不知道它真正适合 Python 模块命名空间的位置(这对于通过-m 开关,但直接执行文件和交互式解释器完全不走运)。

要进一步了解,请参阅Python 3 中的相对导入以获取详细说明以及如何完成它。

于 2021-01-18T21:49:33.483 回答
-2

因为你的代码中包含if __name__ == "__main__",它没有作为包导入,所以你最好用它sys.path.append()来解决问题。

于 2019-10-31T12:38:58.600 回答