这里似乎已经有了一些关于python 3中相对导入的问题,但在浏览了许多之后,我仍然没有找到我的问题的答案。 问题来了。

我有一个如下所示的包

package/
   __init__.py
   A/
      __init__.py
      foo.py
   test_A/
      __init__.py
      test.py

我在test.py中有一行:

from ..A import foo

现在,我在包的文件夹,我运行

python -m test_A.test

我收到消息

"ValueError: attempted relative import beyond top-level package"

但是如果我在包的父文件夹中,例如,我运行:

cd ..
python -m package.test_A.test

一切都很好。

现在我的问题是: 当我在包的文件夹中,我运行test_A子包中的模块作为test_A。测试,根据我的理解,..A只上升了一层,仍然在包文件夹中,为什么它给出消息说在顶层包之外。导致此错误消息的确切原因是什么?


当前回答

编辑:2020-05-08:似乎我引用的网站不再由写建议的人控制,所以我删除了该网站的链接。谢谢你让我知道baxx。


如果有人在已经提供的精彩答案后仍然有点纠结,我在一个网站上找到了不再可用的建议。

我提到的网站的重要引用:

“同样可以通过编程方式指定: 导入系统 sys.path.append (' . ') 当然,上面的代码必须在另一个导入之前编写 声明。

很明显,事情是这样的,事后想想。我试图在我的测试中使用sys.path.append('..'),但遇到了op发布的问题。路径定义之前我的其他导入,我能够解决这个问题。

其他回答

这些解决方案在3.6中都不适合我,文件夹结构如下:

package1/
    subpackage1/
        module1.py
package2/
    subpackage2/
        module2.py

我的目标是从module1导入到module2。奇怪的是,最后对我有用的是:

import sys
sys.path.append(".")

注意单点解决方案,而不是迄今为止提到的两点解决方案。


编辑:以下内容帮助我澄清了这一点:

import os
print (os.getcwd())

在我的例子中,工作目录(出乎意料地)是项目的根目录。

只需移除…在test.py 对我来说,pytest很好地解决了这个问题 例子:

from A import foo
import sys
sys.path.append("..") # Adds higher directory to python modules path.

试试这个。 为我工作。

我对这个问题的理解是这样的:

[案例1]当你开始一个绝对导入

python -m test_A.test

or

import test_A.test

or

from test_A import test

你实际上是将导入锚设置为test_A,换句话说,顶级包是test_A。当我们有test。py do from。一个导入xxx,你正在从锚转义,而Python不允许这样做。

[案例2]当你这样做的时候

python -m package.test_A.test

or

from package.test_A import test

你的锚变成了包,所以包/test_A/test.py从…导入xxx不会转义锚(仍然在包文件夹内),Python很乐意接受这一点。

简而言之:

Absolute-import改变当前锚(=重新定义什么是顶级包); 相对导入不会改变锚,而是限定在锚上。

此外,我们可以使用全限定模块名(FQMN)来检查这个问题。

检查每种情况下的FQMN:

【例2】测试。__name__ = package.test_A.test (CASE1)测试。__name__ = test_A.test

对于CASE2, an。import xxx将生成一个带有FQMN=package的新模块。Xxx,这个可以接受。

对于CASE1, ..从内部从…import xxx将跳出test_A的起始节点(锚点),这是Python不允许的。

我认为这种“相对导入”的限制是一种相当丑陋的设计,完全违背了Python的格言“简单比复杂好”。

编辑:2020-05-08:似乎我引用的网站不再由写建议的人控制,所以我删除了该网站的链接。谢谢你让我知道baxx。


如果有人在已经提供的精彩答案后仍然有点纠结,我在一个网站上找到了不再可用的建议。

我提到的网站的重要引用:

“同样可以通过编程方式指定: 导入系统 sys.path.append (' . ') 当然,上面的代码必须在另一个导入之前编写 声明。

很明显,事情是这样的,事后想想。我试图在我的测试中使用sys.path.append('..'),但遇到了op发布的问题。路径定义之前我的其他导入,我能够解决这个问题。