[英]How to fix "Attempted relative import in non-package" even with __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
环顾四周,我发现“ 即使使用 __init__.py 相对路径也不起作用”和“ 从相对路径导入模块”,但它们没有帮助。
我在这里有什么遗漏吗?
详细说明Ignacio Vazquez-Abrams 的回答:
Python 导入机制相对于当前文件的__name__
。 当你直接执行一个文件时,它没有它通常的名字,而是用"__main__"
作为它的名字。 所以相对进口不起作用。
正如 Igancio 建议的那样,您可以使用-m
选项执行它。 如果您的包的一部分打算作为脚本运行,您还可以使用__package__
属性来告诉该文件它应该在包层次结构中具有什么名称。
有关详细信息,请参阅http://www.python.org/dev/peps/pep-0366/ 。
是的。 你没有把它当作一个包来使用。
python -m pkg.tests.core_test
这取决于您要如何启动脚本。
如果您想以经典方式从命令行启动 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
如果将当前目录附加到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__))))
在 core_test.py 中,执行以下操作:
import sys
sys.path.append('../components')
from core import GameLoopEvents
如果您的用例用于运行测试,并且确实如此,那么您可以执行以下操作。 不要将测试脚本作为python core_test.py
运行,而是使用测试框架,例如pytest
。 然后可以在命令行输入
$$ py.test
这将在您的目录中运行测试。 这解决了@BrenBarn 指出的__name__
是__main__
的问题。 接下来,将一个空的__init__.py
文件放入您的测试目录,这将使测试目录成为您包的一部分。 然后你就可以做
from ..components.core import GameLoopEvents
但是,如果您将测试脚本作为主程序运行,那么事情将再次失败。 所以只需使用测试运行器。 也许这也适用于其他测试运行程序,例如nosetests
,但我还没有检查过。 希望这可以帮助。
问题在于您的测试方法,
你试过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
光盘包装
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
我的快速修复是将目录添加到路径:
import sys
sys.path.insert(0, '../components/')
正如Paolo所说,我们有 2 种调用方法:
1) python -m tests.core_test
2) python tests/core_test.py
它们之间的一个区别是 sys.path[0] 字符串。 由于在进行 import 时解释器会搜索 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
<other stuff>
在此之后,我们可以使用其他方法运行 core_test.py:
cd tests
python core_test.py
python -m core_test
...
注意,py36 仅测试过。
旧线程。 我发现将__all__= ['submodule', ...]
添加到__init__.py文件,然后在目标中使用from <CURRENT_MODULE> import *
可以正常工作。
这种方法对我有用,并且比某些解决方案更简洁:
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 中都适用于我:
try:
from ..components.core import GameLoopEvents
except ( ValueError, ImportError):
from components.core import GameLoopEvents
尝试这个
import components
from components import *
由于您已经将所有内容标记为模块,因此如果您作为 python 模块启动,则无需使用相对引用。
代替
from ..components.core import GameLoopEvents
简单地
from pkg.components.core import GameLoopEvents
当您从 pkg 的父级运行时,请使用以下命令
python -m pkg.tests.core_test
如果有人正在寻找解决方法,我偶然发现了一个。 这里有一点上下文。 我想测试我在文件中的一种方法。 当我从内部运行它时
if __name__ == "__main__":
它总是抱怨相对进口。 我尝试应用上述解决方案,但未能奏效,因为有许多嵌套文件,每个文件都有多个导入。
这就是我所做的。 我刚刚创建了一个启动器,一个可以导入必要方法并调用它们的外部程序。 虽然,这不是一个很好的解决方案,但它确实有效。
这是一种会激怒所有人但效果很好的方法。 在测试运行中:
ln -s ../components components
然后像往常一样导入组件。
对我来说只有这个工作:我必须明确地将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
直接运行我的脚本。
python <main module>.py
不适用于相对导入问题是当您从命令行运行__main__
模块时, 相对导入不起作用
python <main_module>.py
PEP 338中明确说明了这一点。
2.5b1 的发布显示了此 PEP 和 PEP 328 之间令人惊讶的(尽管回想起来很明显)交互 -显式相对导入在主模块中不起作用。 这是因为相对导入依赖于
__name__
来确定当前模块在包层次结构中的位置。 在主模块中,__name__
的值始终为'__main__'
,因此显式相对导入总是会失败(因为它们仅适用于包内的模块)。
这个问题实际上并不是 -m 开关所独有的。 问题是相对导入基于
__name__
,并且在主模块中,__name__
始终具有值__main__
。 因此,相对导入目前无法从应用程序的主模块正常工作,因为主模块不知道它真正适合 Python 模块命名空间的位置(这对于通过-m 开关,但直接执行文件和交互式解释器完全不走运)。
要进一步了解,请参阅Python 3 中的相对导入以获取详细说明以及如何完成它。
这非常令人困惑,如果您使用的是 pycharm 之类的 IDE,那就更令人困惑了。 什么对我有用: 1. 进行 pycharm 项目设置(如果你从 VE 或 python 目录运行 python) 2. 你定义的方式没有错。 有时它适用于 from folder1.file1 导入类
如果它不起作用,请使用 import folder1.file1 3. 您的环境变量应在系统中正确提及或在命令行参数中提供。
我遇到过类似的问题,作为一名软件工程师,我认为这里建议的一些解决方案并不理想。 如果你想要相对导入,你不应该尝试/除了然后有时做绝对导入。 此外,要运行程序,您不必更改 sys.path。
此外,该程序应该始终工作,独立于您当前的工作目录,也独立于您如何启动它。
因此,我创建了一个新的、实验性的导入库: ultraimport它允许基于文件系统的导入,无论您如何运行代码。
从最初的问题,您可以将 core_test.py 更改为类似
import ultraimport
GameLoopEvents = ultraimport('__dir__/../components/core.py', 'GameLoopEvents')
print(GameLoopEvents)
无论您如何运行测试,它总会找到它。
$ python -m tests.core_test
<class 'core.GameLoopEvents'>
python ./tests/core_test.py
<class 'core.GameLoopEvents'>
我也将此示例放入 git repo 的示例文件夹中。
由于图书馆是实验性的,我对反馈很感兴趣。 它对我有用,但尚未经过广泛测试。
如果您的项目结构如下所示:
project
|
| --- module1
| |
| file1.py
|
|-----module2
| |
| file2.py
并且您将从 file2.py 中导入 file1.py,您可以在 file2.py 中执行此操作:
import sys
sys.path.append('.')
import file2
我仍然不知道为什么以及如何,但它对我有用。
因为您的代码中包含if __name__ == "__main__"
,它不会作为包导入,所以您最好使用sys.path.append()
来解决问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.