python中__init__导入失败_python - 如何使用__init__.py修复“在非包中尝试相对导入”...

当在Python中遇到'在非包中尝试相对导入'的错误时,问题通常在于没有正确使用__init__.py文件。解决方案包括使用python -m选项执行脚本,调整sys.path,或者在脚本中设置__package__属性。确保在需要作为包导入的目录下包含__init__.py。

python - 如何使用__init__.py修复“在非包中尝试相对导入”

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

pkg/

__init__.py

components/

core.py

__init__.py

tests/

core_test.py

__init__.py

在core_test.py我有以下import语句

from ..components.core import GameLoopEvents

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

tests$ python core_test.py

Traceback (most recent call last):

File "core_test.py", line 3, in

from ..components.core import GameLoopEvents

ValueError: Attempted relative import in non-package

在我周围搜索时发现“相对路径甚至无法使用__init__.py”和“从相对路径导入模块”,但它们没有帮助。

这里有什么我想念的吗?

12个解决方案

561 votes

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

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

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

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

BrenBarn answered 2018-12-24T02:59:38Z

382 votes

是。 您没有将它用作包。

python -m pkg.tests.core_test

Ignacio Vazquez-Abrams answered 2018-12-24T02:59:04Z

195 votes

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

ihm answered 2018-12-24T02:59:59Z

165 votes

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

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

python tests/core_test.py

然后,因为在这种情况下'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

否则,您可以使用'-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

Paolo Rovelli answered 2018-12-24T03:00:41Z

14 votes

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

import sys

sys.path.append('../components')

from core import GameLoopEvents

Allan Mwesigwa answered 2018-12-24T03:01:01Z

9 votes

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

$$ py.test

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

from ..components.core import GameLoopEvents

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

deepak answered 2018-12-24T03:01:31Z

5 votes

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

import sys

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

v4gil answered 2018-12-24T03:01:51Z

2 votes

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

Laurent answered 2018-12-24T03:02:11Z

0 votes

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

if __name__ == "__main__":

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

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

HappyWaters answered 2018-12-24T03:02:41Z

0 votes

试试这个

import components

from components import *

Vaishnavi Bala answered 2018-12-24T03:03:01Z

0 votes

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

O8dKL.png

Jayhello answered 2018-12-24T03:03:21Z

0 votes

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

1) python -m tests.core_test

2) python tests/core_test.py

它们之间的一个区别是sys.path [0]字符串。 由于解释将在导入时搜索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

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

cd tests

python core_test.py

python -m core_test

...

注意,py36只测试过。

zhengcao answered 2018-12-24T03:03:54Z

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值