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

sky*_*der 713 python init package python-import importerror

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

pkg/
  __init__.py
  components/
    core.py
    __init__.py
  tests/
    core_test.py
    __init__.py
Run Code Online (Sandbox Code Playgroud)

在core_test.py我有以下import语句

from ..components.core import GameLoopEvents
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

搜索周围我发现" 相对路径甚至不能使用__init__.py "和" 从相对路径导入模块 ",但它们没有帮助.

这里有什么我想念的吗?

Bre*_*arn 612

详细阐述Ignacio Vazquez-Abrams的答案:

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

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

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

  • 花了一段时间让我意识到你不能从`tests`子目录中运行`python -m core_test` - 它必须来自父节点,或者你必须将父节点添加到路径中. (53认同)
  • @DannyStaple:嗯,它的工作原理在链接文档中有所描述.如果你在`pack.subpack`包中有一个脚本`script.py`,那么将它的`__package__`设置为`pack.subpack`将允许你从`.module导入一些东西`来从`pack.module导入一些东西`.请注意,正如文档所述,您仍然必须在系统路径上拥有顶级包.这已经成为导入模块的工作方式.`_package__`唯一能做的就是让你将这种行为用于直接执行的脚本. (4认同)
  • @DannyStaple:不完全是.您可以使用`__package__`来确保可执行脚本文件可以从相同的包中相对导入其他模块.没有办法从"整个系统"相对导入.我甚至不确定你为什么要这样做. (3认同)
  • 我在直接执行的脚本中使用了“ __package__”,但是不幸的是,出现以下错误:“未加载父模块'xxx',无法执行相对导入” (3认同)
  • 我的意思是如果`__package__`符号设置为"parent.child",那么你就可以导入"parent.other_child".也许我没有这么说. (2认同)

Ign*_*ams 426

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

python -m pkg.tests.core_test
Run Code Online (Sandbox Code Playgroud)

  • 考虑到这个问题和答案的流行,我不是其中的任何一个,但我觉得这可以使用*相当多的*更多细节.注意到从什么目录执行上面的shell命令,你需要`__init __.py一直向下,以及`__package__`修改技巧(下面由BrenBarn描述)需要允许这些导入可执行文件脚本(例如,当使用shebang并在Unix shell上执行`./ my_script.py`时)都会很有用.对于我来说,找出或找到简洁易懂的文档对整个问题来说非常棘手. (472认同)
  • 说真的,你能解释一下你的答案中会发生什么吗? (86认同)
  • 问题:请注意,最后没有'.py'! (49认同)
  • 注意:您需要在CLI中调用此行的位置`pkg`之外.然后,它应该按预期工作.如果你在`pkg`里面,你调用`python -m tests.core_test`,它将无法正常工作.至少它不适合我. (15认同)
  • @MarkAmery几乎失去了我的想法试图弄清楚这一切是如何工作的,项目中的相对导入与具有`__init __.py`文件的py文件的子目录但你仍然得到`ValueError:尝试非包中的相对导入'错误.我会为某个人付出非常好的钱,在某个地方,最后用简单的英语解释所有这些是如何工作的. (15认同)
  • 例如,如果您在组件dir中运行脚本,则这不起作用 (5认同)
  • 有一个[下面很好地阐述了最近的答案](http://stackoverflow.com/a/27876800/838992),其中包含了upvoted评论建议的文档链接. (3认同)
  • 与Python中的FAQ的答案相反,这个答案并不清楚发生了什么以及执行此命令的位置 (2认同)
  • 我不确定将其作为模块运行如何帮助 @skytreader 从另一个脚本中导入问题...... (2认同)
  • 也许我不太了解 SO 上类似 wiki 的功能的文化规范,但值得注意的是,没有人指出如何改进 Ignacio 的答案实际上就地改进了它。(请注意,我 100% 同意解释会非常有帮助的评论。) (2认同)

ihm*_*ihm 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__))))
Run Code Online (Sandbox Code Playgroud)

  • `sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__ file __),'..')))`这个也行 (34认同)
  • `from os import sys`看起来像作弊:) (23认同)
  • 仅供参考,为了在ipython笔记本中使用它,我将这个答案改编为:`import os; os.sys.path.append(os.path.dirname(os.path.abspath则( '')))`.然后直接`import components.core`适用于我,根据需要从笔记本的父目录导入. (10认同)
  • @flyingsheep:同意,我只是使用常规`import sys,os.path作为路径`. (7认同)
  • @Piotr:它可能被认为更好,因为它稍微清楚地表明了什么是附加到`sys.path` - 当前文件所在目录的父目录. (3认同)
  • @ajay和你的更好因为什么? (2认同)
  • @martineau我认为你是[正确](https://github.com/pfmoore/pip/commit/87418e05bb55ea64476653dd41e2f4db785f02c0#commitcomment-5418772):) (2认同)
  • @Piotr:啊,是的,我必须同意`sys.path.append(path.dirname(path.dirname(__ file __)))`会更清楚 - 并且评论完美. (2认同)

Pao*_*lli 178

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

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

python tests/core_test.py
Run Code Online (Sandbox Code Playgroud)

然后,因为在这种情况下'components'和'tests'是兄弟文件夹,你可以使用sys.path模块的insert或append方法导入相关模块.就像是:

import sys
from os import path
sys.path.append( path.dirname( path.dirname( path.abspath(__file__) ) ) )
from components.core import GameLoopEvents
Run Code Online (Sandbox Code Playgroud)

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

python -m pkg.tests.core_test
Run Code Online (Sandbox Code Playgroud)

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

from ..components.core import GameLoopEvents
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

  • 如果我尝试使用pdb进行调试,该怎么办?因为你使用`python -m pdb myscript.py`来启动调试会话. (3认同)
  • 使用`insert`而不是`append`会更好吗?也就是说,`sys.path.insert(0,os.path.dirname(os.path.dirname(os.path.abspath(__ file __))))` (3认同)
  • 使用insert可以更好地匹配相对导入语义,其中本地包名称优先于已安装的包.特别是对于测试,您通常要测试本地版本,而不是已安装的版本(除非您的测试基础架构安装了测试中的代码,在这种情况下,不需要相关导入,您将不会遇到此问题). (2认同)
  • 您还应该提到,当您作为模块运行时,您不能位于包含 core_test 的目录中(这太简单了) (2认同)

All*_*gwa 20

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

import sys
sys.path.append('../components')
from core import GameLoopEvents
Run Code Online (Sandbox Code Playgroud)


Moh*_*med 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
Run Code Online (Sandbox Code Playgroud)

光盘包

python -m tests.core_test # dont use .py
Run Code Online (Sandbox Code Playgroud)

或来自外部 pkg/

python -m pkg.tests.core_test
Run Code Online (Sandbox Code Playgroud)

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

hi/
  hello.py
how.py
Run Code Online (Sandbox Code Playgroud)

在 how.py

from .hi import hello
Run Code Online (Sandbox Code Playgroud)

如果你想从 hello.py 导入 how

from .. import how
Run Code Online (Sandbox Code Playgroud)


dee*_*pak 9

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

$$ py.test
Run Code Online (Sandbox Code Playgroud)

这将在您的目录中运行测试.这得到周围人的问题__name__的存在__main__,是由@BrenBarn指出.接下来,将一个空__init__.py文件放入您的测试目录中,这将使测试目录成为您的包的一部分.然后你就可以做到

from ..components.core import GameLoopEvents
Run Code Online (Sandbox Code Playgroud)

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


v4g*_*gil 7

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

import sys
sys.path.insert(0, '../components/')
Run Code Online (Sandbox Code Playgroud)

  • 您的方法在所有情况下都不起作用,因为'../'部分是从您运行脚本的目录(core_test.py)中解析的.使用您的方法,您必须在运行core_test.py scritp之前cd到'tests'. (5认同)

sur*_*nto 7

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

代替

from ..components.core import GameLoopEvents
Run Code Online (Sandbox Code Playgroud)

简单地

from pkg.components.core import GameLoopEvents
Run Code Online (Sandbox Code Playgroud)

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

python -m pkg.tests.core_test
Run Code Online (Sandbox Code Playgroud)