相对导入时超出顶级包错误
- 2024-12-23 08:39:00
- admin 原创
- 52
问题描述:
似乎这里已经有很多关于 python 3 中的相对导入的问题,但是经过许多问题之后,我仍然没有找到我的问题的答案。所以这是问题。
我有一个如下所示的包
package/
__init__.py
A/
__init__.py
foo.py
test_A/
__init__.py
test.py
我在 test.py 中有一行:
from ..A import foo
现在,我在文件夹中package
,然后运行
python -m test_A.test
我收到消息
"ValueError: attempted relative import beyond top-level package"
但如果我在的父文件夹中package
,例如,我运行:
cd ..
python -m package.test_A.test
一切都很好。
现在我的问题是:
当我在文件夹中时package
,我运行 test_A 子包内的模块test_A.test
,根据我的理解,..A
只上升一个级别,仍然在package
文件夹内,为什么它会给出消息说beyond top-level package
。导致此错误消息的确切原因是什么?
解决方案 1:
编辑:7 年后我重新审视了这个问题,并有了更好的理解。我把原来的答案留在这里,但我在这里写了一个更通用/更全面的答案。特别是,我现在知道了我在原始答案中提出的最后一个问题的答案。
其他更连贯的答案:
兄弟包导入
相对进口量达十亿次
为什么不起作用?这是因为 python 不记录包是从哪里加载的。所以当你这样做时,它基本上只是丢弃实际存储在 中的python -m test_A.test
知识(即不被视为包)。尝试是试图访问它不再拥有的信息(即加载位置的兄弟目录)。它在概念上类似于允许中的文件。这会很糟糕,因为您希望包是不同的。如果他们需要使用另一个包中的东西,那么他们应该用 全局引用它们,并让 python 用和找出它在哪里。test_A.test
`packagepackage
from ..A import foofrom ..os import path
mathfrom os import path
$PATH`$PYTHONPATH
当您使用时python -m package.test_A.test
,使用就from ..A import foo
可以很好地解析,因为它会跟踪其中的内容package
,而您只是访问已加载位置的子目录。
为什么 python 不将当前工作目录视为包? 不知道,但天哪,这很有用。
解决方案 2:
import sys
sys.path.append("..") # Adds higher directory to python modules path.
试试这个。对我有用。
解决方案 3:
假设:
如果您在package
目录中,A
并且test_A
是单独的包。
结论:
..A
仅允许在包内导入。
进一步说明:
如果您想强制将包放置在位于的任何路径上,则使相对导入仅在包内可用很有用sys.path
。
编辑:
只有我一个人觉得这太疯狂了吗?!为什么当前工作目录不被视为包? – Multihunter
当前工作目录通常位于 sys.path 中。因此,那里的所有文件都是可导入的。这是自 Python 2 以来的行为,当时包尚不存在。将运行目录设为包将允许以“import .A”和“import A”的形式导入模块,然后这将是两个不同的模块。也许这是一个需要考虑的不一致之处。
解决方案 4:
在 3.6 中,这些解决方案都不适用于我,其文件夹结构如下:
package1/
subpackage1/
module1.py
package2/
subpackage2/
module2.py
我的目标是从 module1 导入到 module2。奇怪的是,最终对我有用的是:
import sys
sys.path.append(".")
请注意单点,而不是迄今为止提到的双点解决方案。
编辑:以下内容帮助我澄清了这一点:
import os
print (os.getcwd())
就我而言,工作目录(意外地)是项目的根目录。
解决方案 5:
这在 Python 中非常棘手。
我将首先评论您遇到该问题的原因,然后我会提到两种可能的解决方案。
这是怎么回事?
您必须考虑Python文档中的这一段:
请注意,相对导入基于当前模块的名称。由于主模块的名称始终为“ main ”,因此打算用作 Python 应用程序主模块的模块必须始终使用绝对导入。
另外还有来自PEP 328 的以下内容:
相对导入使用模块的名称属性来确定模块在软件包层次结构中的位置。如果模块的名称不包含任何软件包信息(例如,设置为“ main ”),则相对导入将被视为模块是顶级模块,而不管模块在文件系统上的实际位置。
相对导入从文件名(__name__
属性)开始,它可以采用两个值:
它是文件名,前面是文件夹结构,用点分隔。例如:
package.test_A.test
这里 Python 知道父目录:beforetest
是test_A
thenpackage
。
因此,您可以使用点符号进行相对导入。
# package.test_A/test.py
from ..A import foo
然后,您可以在根目录中拥有一个类似根文件的文件,它调用test.py
:
# root.py
from package.test_A import test
当您直接运行模块 (
test.py
) 时,它将成为程序的入口点,因此__name__
==__main__
。文件名没有指示目录结构,因此 Python 不知道如何在目录中向上移动。对于 Python 来说,test.py
成为顶级脚本,它上面没有任何内容。这就是您不能使用相对导入的原因。
可能的解决方案
解决这个问题的一种方法是有一个根文件(在根目录中)来调用模块/包,如下所示:
root.py
导入test.py
。(入口点,__name__ == __main__
)。test.py
(相对)进口foo.py
。foo.py
表示模块已被导入。
输出为:
package.A.foo has been imported
Module's name is: package.test_A.test
B)如果您想将模块作为顶级(独立)脚本执行,您可以从命令行尝试执行以下操作:
python -m package.test_A.test
您还应该检查:第十亿次的相对进口,特别是BrenBarn的答案。
解决方案 6:
from package.A import foo
我认为这比
import sys
sys.path.append("..")
解决方案 7:
正如最受欢迎的答案所暗示的那样,基本上是因为您的PYTHONPATH
或sys.path
包含.
但不是您的包的路径。而且相对导入是相对于您当前的工作目录,而不是导入发生的文件;奇怪的是。
您可以通过首先将相对导入更改为绝对导入,然后使用以下命令启动它来解决:
PYTHONPATH=/path/to/package python -m test_A.test
或者以这种方式调用时强制使用 python 路径,因为:
您python -m test_A.test
正在执行test_A/test.py
并__name__ == '__main__'
`__file__ == '/absolute/path/to/test_A/test.py'`
这意味着test.py
您可以在主要情况中使用绝对import
半保护,也可以进行一些一次性 Python 路径操作:
from os import path
…
def main():
…
if __name__ == '__main__':
import sys
sys.path.append(path.join(path.dirname(__file__), '..'))
from A import foo
exit(main())
解决方案 8:
这实际上比其他答案说的要简单得多。
TL;DR:A
直接导入,而不是尝试相对导入。
当前工作目录不是包,除非您从其他文件夹导入该文件夹package
。因此,如果您打算让其他应用程序导入包,则包的行为将正常工作。不起作用的是测试...
无需改变目录结构中的任何内容,只需改变如何test.py
导入foo.py
。
from A import foo
现在python -m test_A.test
从package
目录运行将无需ImportError
。
为什么这样做有效?
您当前的工作目录不是包,但它被添加到路径中。因此,您可以A
直接导入文件夹及其内容。这与您可以导入已安装的任何其他包的原因相同……它们都包含在您的路径中。
解决方案 9:
编辑:2020-05-08:似乎我引用的网站不再由撰写建议的人控制,所以我删除了该网站的链接。感谢 baxx 告知我。
如果有人在阅读完前面提供的出色答案后仍然感到有些困惑,我在一个不再可用的网站上找到了建议。
我提到的网站上的重要引言:
“可以通过以下方式以编程方式指定相同内容:
导入系统
sys.path.append('..')
当然,上面的代码必须写在其他导入
语句之前。
事后想想,很明显必须这样。我试图在测试中使用 sys.path.append('..'),但遇到了 OP 发布的问题。通过在其他导入之前添加导入和 sys.path 定义,我能够解决问题。
解决方案 10:
..
只需在 test.py 中
删除即可。对我来说,pytest 可以很好地运行,
例如:
from A import foo
解决方案 11:
如果您在上层文件夹中有一个__init__.py
,则可以像在该 init 文件中一样初始化导入import file/path as alias
。然后,您可以在下层脚本中使用它,如下所示:
import alias
解决方案 12:
就我而言,我必须改成这样:解决方案 1(更好的是,它依赖于当前的 py 文件路径。易于部署)使用pathlib.Path.parents 使代码更简洁
import sys
import os
import pathlib
target_path = pathlib.Path(os.path.abspath(__file__)).parents[3]
sys.path.append(target_path)
from utils import MultiFileAllowed
解决方案 2
import sys
import os
sys.path.append(os.getcwd())
from utils import MultiFileAllowed
解决方案 13:
以我个人的拙见,我是这么理解这个问题的:
[案例 1] 当你启动绝对导入时,例如
python -m test_A.test
或者
import test_A.test
或者
from test_A import test
您实际上将import-anchor设置为test_A
,换句话说,顶级包是test_A
。因此,当我们有 test.py do 时from ..A import xxx
,您正在脱离锚点,而 Python 不允许这样做。
[案例 2] 当你这样做
python -m package.test_A.test
或者
from package.test_A import test
你的锚点变成了package
,这样package/test_A/test.py
做from ..A import xxx
并不会逃离锚点(仍然在package
文件夹内),Python 很乐意接受这一点。
简而言之:
绝对导入改变当前锚点(=重新定义什么是顶级包);
相对导入不会改变锚点但会将其限制在其中。
此外,我们可以使用完全限定模块名称(FQMN)来检查这个问题。
检查每种情况下的 FQMN:
[案例2]
test.__name__
=package.test_A.test
[案例1]
test.__name__
=test_A.test
因此,对于 CASE2,from .. import xxx
将产生一个具有 FQMN= 的新模块package.xxx
,这是可以接受的。
而对于CASE1来说,..
from 内部from .. import xxx
会跳出 的起始节点(锚点)test_A
,这是Python所不允许的。
[2022-07-19] 我认为这个“相对导入”限制是一个相当丑陋的设计,完全违背了 Python 的座右铭“简单胜于复杂”之一。
解决方案 14:
拥有
package/
__init__.py
A/
__init__.py
foo.py
test_A/
__init__.py
test.py
A/__init__.py
导入时foo
:
from .foo import foo
A/
从导入时test_A/
import sys, os
sys.path.append(os.path.abspath('../A'))
# then import foo
import foo
解决方案 15:
不确定在 python 2.x 中,但在 python 3.6 中,假设你试图运行整个套件,你只需要使用-t
-t, --top-level-directory 目录 项目的顶级目录(默认为起始目录)
因此,在像这样的结构上
project_root
|
|----- my_module
| \n | _____ my_class.py
|
tests
___ test_my_func.py
例如可以使用:
python3 unittest discover -s /full_path/project_root/tests -t /full_path/project_root/
并且仍然导入my_module.my_class
无重大戏剧。
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理必备:盘点2024年13款好用的项目管理软件