如何解决“在非包中尝试相对导入”,即使使用了__init__.py

我试图按照以下目录结构遵循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 

search我发现“ 相对path不工作,即使__init__.py ”和“ 从相对path导入模块 ”,但他们没有帮助。

有什么我在这里失踪?

是。 你不是把它作为一个包装。

 python -m pkg.tests.core_test 

详细说明@ Ignacio的回答:

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

你可以像Iganciobuild议的那样,使用-m选项来执行它。 如果你的软件包的一部分是作为脚本来运行的,你也可以使用__package__属性来告诉这个文件在软件包层次结构中应该有什么名字。

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

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

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

这取决于你想如何启动你的脚本。

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

 python tests/core_test.py 

然后,因为在这种情况下, “组件”“testing”是兄弟文件夹,您可以使用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 

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

 $$ py.test 

这将运行您的目录中的testing。 这解决了__name__指出__name____main__的问题。 接下来,将一个空的__init__.py文件放到您的testing目录中,这将使testing目录成为您的软件包的一部分。 那你就可以做了

 from ..components.core import GameLoopEvents 

但是,如果将testing脚本作为主程序运行,那么事情将再次失败。 所以只需使用testing跑步者。 也许这也适用于其他testing运动员,如noseteststesting,但我没有检查它。 希望这可以帮助。

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

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

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

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