关于python:如何修复”尝试在非包中进行相对导入Attempted relative import in non-package”,即使使用 __init__.py

How to fix “Attempted relative import in non-package” even with __init__.py

我正试图遵循PEP 328,目录结构如下:

1
2
3
4
5
6
7
8
pkg/
  __init__.py
  components/
    core.py
    __init__.py
  tests/
    core_test.py
    __init__.py

core_test.py中,我有以下进口声明

1
from ..components.core import GameLoopEvents

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

1
2
3
4
5
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

在四处搜索时,我发现"相对路径即使与uuu init_uuy.py"和"从相对路径导入模块"都不起作用,但它们没有帮助。

我这里有什么东西不见了吗?


详细说明伊格纳西奥·瓦兹奎兹·艾布拉姆斯的回答:

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

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

详情请参见http://www.python.org/dev/peps/pep-0366/。


对。你不能把它当作包裹使用。

1
python -m pkg.tests.core_test


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

1
2
3
if __name__ == '__main__' and __package__ is None:
    from os import sys, path
    sys.path.append(path.dirname(path.dirname(path.abspath(__file__))))


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

如果您想以一种经典的方式从命令行启动UnitTest,那就是:

1
python tests/core_test.py

然后,因为在本例中,"components"和"tests"是兄弟文件夹,所以可以使用sys.path模块的insert或append方法导入相对模块。类似:

1
2
3
4
import sys
from os import path
sys.path.append( path.dirname( path.dirname( path.abspath(__file__) ) ) )
from components.core import GameLoopEvents

否则,可以使用"-m"参数启动脚本(请注意,在本例中,我们讨论的是一个包,因此不能提供".py"扩展名),即:

1
python -m pkg.tests.core_test

在这种情况下,您只需使用相对导入即可:

1
from ..components.core import GameLoopEvents

最后,您可以混合使用这两种方法,这样无论脚本如何调用,它都可以工作。例如:

1
2
3
4
5
6
7
8
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


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

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

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

1
$$ py.test

将运行目录中的测试。这就解决了@brenbarn指出的__name____main__的问题。接下来,将一个空的__init__.py文件放入测试目录,这将使测试目录成为包的一部分。那你就能做到了

1
from ..components.core import GameLoopEvents

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


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

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


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

enter image description here


老线程。我发现在_初始化文件,然后在目标中使用from import *就可以了。


正如paolo所说,我们有两个调用方法:

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

它们之间的一个区别是sys.path[0]字符串。由于在执行导入时,解释将搜索sys.path,因此我们可以使用tests/core_test.py

1
2
3
4
5
6
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:

1
2
3
4
cd tests
python core_test.py
python -m core_test
...

注:仅PY36测试。


试试这个

1
2
import components
from components import *


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

1
if __name__ =="__main__":

它总是抱怨相对进口。我尝试应用上面的解决方案,但没有成功,因为有许多嵌套文件,每个都有多个导入。

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