如何修复“非包装中的尝试相对导入”,即使使用__init__.py也是如此

时间:2012-07-18 07:59:14

标签: python package python-import importerror init

我正在尝试关注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

搜索我发现“relative path not working even with __init__.py”和“Import a module from a relative path”,但他们没有帮助。

这里有什么我想念的吗?

20 个答案:

答案 0 :(得分:588)

详细说明Ignacio Vazquez-Abrams's回答:

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

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

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

答案 1 :(得分:400)

是。你没有把它作为一个包使用。

python -m pkg.tests.core_test

答案 2 :(得分:198)

如果将当前目录附加到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__))))

答案 3 :(得分:171)

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

如果你想以经典的方式launch your UnitTest from the command line,那就是:

python tests/core_test.py

然后,由于在这种情况下&#39; &#39; &#39; &#39; 是兄弟文件夹,您可以导入相关模块使用插入 sys.path 模块的追加方法。 类似的东西:

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

否则,您可以launch your script with the '-m' argument(请注意,在这种情况下,我们正在谈论一个包,因此您不能提供&#39; .py&#39; 扩展名),即:

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

答案 4 :(得分:18)

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

import sys
sys.path.append('../components')
from core import GameLoopEvents

答案 5 :(得分:9)

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

$$ py.test

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

from ..components.core import GameLoopEvents

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

答案 6 :(得分:7)

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

import sys
sys.path.insert(0, '../components/')

答案 7 :(得分:2)

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

enter image description here

答案 8 :(得分:2)

旧线程。我发现在{。}添加__all__= ['submodule', ...] __ init __。py 文件然后使用目标中的from <CURRENT_MODULE> import *工作正常。

答案 9 :(得分:1)

如果有人正在寻找解决方法,我偶然发现了一个。这是一个背景。我想测试一下我在文件中的一种方法。当我从

中运行它时
if __name__ == "__main__":

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

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

答案 10 :(得分:1)

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

代替

from ..components.core import GameLoopEvents

简单

from pkg.components.core import GameLoopEvents

从pkg的父级运行时,请使用以下内容

python -m pkg.tests.core_test

答案 11 :(得分:0)

试试这个

import components
from components import *

答案 12 :(得分:0)

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

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

它们之间的一个区别是sys.path [0]字符串。从the interpret will search sys.path when doing import开始,我们可以使用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经过测试。

答案 13 :(得分:0)

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

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中均有效: / p>

try:
  from ..components.core import GameLoopEvents
except ( ValueError, ImportError):
  from components.core import GameLoopEvents

答案 14 :(得分:0)

由于您的代码包含var convertedArray = [0, -1, 12, 456, -512]; (不会作为软件包导入),因此最好使用if __name__ == "__main__"来解决问题。

答案 15 :(得分:0)

问题与您的测试方法有关,

您尝试过python core_test.py

然后您将收到此错误 ValueError:尝试以非打包方式进行相对导入

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

因此从软件包源测试模块。

如果这是您的项目结构,

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

cd pkg

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

答案 16 :(得分:0)

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

ln -s ../components components

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

答案 17 :(得分:0)

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

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

答案 18 :(得分:0)

仅对我而言这有效:我必须将 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直接运行脚本。

答案 19 :(得分:0)

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

问题是当您从命令行运行 __main__ 模块时 relative import 不起作用

python <main_module>.py

PEP 338中有明确说明。

<块引用>

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

原因

<块引用>

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

要进一步了解,请参阅 Relative imports in Python 3 以获取详细说明以及如何解决。