Python 超出相对导入中的顶级包错误
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/30669474/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
beyond top level package error in relative import
提问by shelper
It seems there are already quite some questions here about relative import in python 3, but after going through many of them I still didn't find the answer for my issue. so here is the question.
似乎这里已经有很多关于 python 3 中的相对导入的问题,但是在经历了很多问题之后,我仍然没有找到我的问题的答案。所以这里是问题。
I have a package shown below
我有一个如下所示的包
package/
__init__.py
A/
__init__.py
foo.py
test_A/
__init__.py
test.py
and I have a single line in test.py:
我在 test.py 中有一行:
from ..A import foo
now, I am in the folder of package
, and I run
现在,我在 的文件夹中package
,然后运行
python -m test_A.test
I got message
我收到消息
"ValueError: attempted relative import beyond top-level package"
but if I am in the parent folder of package
, e.g., I run:
但是如果我在 的父文件夹中package
,例如,我运行:
cd ..
python -m package.test_A.test
everything is fine.
一切安好。
Now my question is:when I am in the folder of package
, and I run the module inside the test_A sub-package as test_A.test
, based on my understanding, ..A
goes up only one level, which is still within the package
folder, why it gives message saying beyond top-level package
. What is exactly the reason that causes this error message?
现在我的问题是:当我在 的文件夹中package
,并且我在 test_A 子包中运行模块时test_A.test
,根据我的理解,..A
只上升了一层,仍然在package
文件夹中,为什么它会给出消息说beyond top-level package
. 导致此错误消息的确切原因是什么?
回答by User
Assumption:
If you are in the package
directory, A
and test_A
are separate packages.
假设:
如果你在package
目录中,A
并且test_A
是单独的包。
Conclusion:..A
imports are only allowed within a package.
结论:..A
只允许在包内导入。
Further notes:
Making the relative imports only available within packages is useful if you want to force that packages can be placed on any path located on sys.path
.
进一步说明:
如果您想强制将包放置在位于sys.path
.
EDIT:
编辑:
Am I the only one who thinks that this is insane!? Why in the world is the current working directory not considered to be a package? – Multihunter
只有我一个人觉得这很疯狂吗!?为什么当前的工作目录不被认为是一个包?–多人猎人
The current working directory is usually located in sys.path. So, all files there are importable. This is behavior since Python 2 when packages did not yet exist. Making the running directory a package would allow imports of modules as "import .A" and as "import A" which then would be two different modules. Maybe this is an inconsistency to consider.
当前工作目录通常位于 sys.path 中。因此,那里的所有文件都是可导入的。这是自 Python 2 以来包尚不存在时的行为。使运行目录成为一个包将允许将模块导入为“import .A”和“import A”,这将是两个不同的模块。也许这是一个需要考虑的不一致之处。
回答by jenish Sakhiya
import sys
sys.path.append("..") # Adds higher directory to python modules path.
Try this. Worked for me.
尝试这个。为我工作。
回答by Multihunter
EDIT: There are better/more coherent answers to this question in other questions:
编辑:在其他问题中对这个问题有更好/更连贯的答案:
Why doesn't it work?It's because python doesn't record where a package was loaded from. So when you do python -m test_A.test
, it basically just discards the knowledge that test_A.test
is actually stored in package
(i.e. package
is not considered a package). Attempting from ..A import foo
is trying to access information it doesn't have any more (i.e. sibling directories of a loaded location). It's conceptually similar to allowing from ..os import path
in a file in math
. This would be bad because you want the packages to be distinct. If they need to use something from another package, then they should refer to them globally with from os import path
and let python work out where that is with $PATH
and $PYTHONPATH
.
为什么不起作用?这是因为 python 没有记录包是从哪里加载的。所以当你这样做时python -m test_A.test
,它基本上只是丢弃test_A.test
实际存储的知识package
(即package
不被视为包)。正在from ..A import foo
尝试访问它不再拥有的信息(即加载位置的同级目录)。它在概念上类似于允许from ..os import path
在math
. 这会很糟糕,因为您希望包是不同的。如果他们需要使用一些从另一个包,那么他们应该是指他们与全球from os import path
,让蟒蛇在哪里工作是与$PATH
和$PYTHONPATH
。
When you use python -m package.test_A.test
, then using from ..A import foo
resolves just fine because it kept track of what's in package
and you're just accessing a child directory of a loaded location.
当您使用python -m package.test_A.test
,然后 usingfrom ..A import foo
解析就好了,因为它会跟踪其中的内容,package
而您只是在访问已加载位置的子目录。
Why doesn't python consider the current working directory to be a package?NO CLUE, but gosh it would be useful.
为什么python不认为当前工作目录是一个包?不知道,但天哪,它会很有用。
回答by Joe Zhow
from package.A import foo
from package.A import foo
I think it's clearer than
我认为它比
import sys
sys.path.append("..")
回答by Mierpo
Edit: 2020-05-08: Is seems the website I quoted is no longer controlled by the person who wrote the advice, so I'm removing the link to the site. Thanks for letting me know baxx.
编辑:2020-05-08:我引用的网站似乎不再由撰写建议的人控制,所以我删除了该网站的链接。谢谢你让我知道baxx。
If someone's still struggling a bit after the great answers already provided, I found advice on a website that no longer is available.
如果有人在已经提供了很好的答案之后仍然有点挣扎,我在一个不再可用的网站上找到了建议。
Essential quote from the site I mentioned:
我提到的网站的基本报价:
"The same can be specified programmatically in this way:
import sys
sys.path.append('..')
Of course the code above must be written before the other importstatement.
“可以通过这种方式以编程方式指定相同的内容:
导入系统
sys.path.append('..')
当然上面的代码必须写在另一个import语句之前。
It's pretty obvious that it has to be this way, thinking on it after the fact. I was trying to use the sys.path.append('..') in my tests, but ran into the issue posted by OP. By adding the import and sys.path defintion before my other imports, I was able to solve the problem.
很明显,它必须是这样,事后考虑它。我试图在我的测试中使用 sys.path.append('..') ,但遇到了 OP 发布的问题。通过在其他导入之前添加导入和 sys.path 定义,我能够解决问题。
回答by pelos
if you have an __init__.py
in an upper folder, you can initialize the import as
import file/path as alias
in that init file. Then you can use it on lower scripts as:
如果您__init__.py
在上层文件夹中有一个,则可以像import file/path as alias
在该 init 文件中一样初始化导入
。然后你可以在较低的脚本上使用它:
import alias
回答by Jason DeMorrow
None of these solutions worked for me in 3.6, with a folder structure like:
这些解决方案在 3.6 中都不适合我,文件夹结构如下:
package1/
subpackage1/
module1.py
package2/
subpackage2/
module2.py
My goal was to import from module1 into module2. What finally worked for me was, oddly enough:
我的目标是从 module1 导入到 module2。最终对我有用的是,奇怪的是:
import sys
sys.path.append(".")
Note the single dot as opposed to the two-dot solutions mentioned so far.
请注意单点而不是目前提到的两点解决方案。
Edit: The following helped clarify this for me:
编辑:以下帮助我澄清了这一点:
import os
print (os.getcwd())
In my case, the working directory was (unexpectedly) the root of the project.
就我而言,工作目录是(出乎意料地)项目的根目录。
回答by dlamblin
As the most popular answer suggests, basically its because your PYTHONPATH
or sys.path
includes .
but not your path to your package. And the relative import is relative to your current working directory, not the file where the import happens; oddly.
正如最受欢迎的答案所暗示的那样,基本上是因为您的PYTHONPATH
或sys.path
包含.
但不是您的包裹路径。而相对导入是相对于你当前的工作目录,而不是导入发生的文件;奇怪。
You could fix this by first changing your relative import to absolute and then either starting it with:
您可以通过首先将相对导入更改为绝对导入,然后以以下方式启动它来解决此问题:
PYTHONPATH=/path/to/package python -m test_A.test
OR forcing the python path when called this way, because:
或以这种方式调用时强制使用 python 路径,因为:
With python -m test_A.test
you're executing test_A/test.py
with __name__ == '__main__'
and __file__ == '/absolute/path/to/test_A/test.py'
随着python -m test_A.test
你在执行test_A/test.py
与__name__ == '__main__'
和__file__ == '/absolute/path/to/test_A/test.py'
That means that in test.py
you could use your absolute import
semi-protected in the main case condition and also do some one-time Python path manipulation:
这意味着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())
回答by Jimm Chen
In my humble opinion, I understand this question in this way:
以我的拙见,我是这样理解这个问题的:
[CASE 1] When you start an absolute-import like
[案例 1] 当您开始绝对导入时
python -m test_A.test
or
或者
import test_A.test
or
或者
from test_A import test
you're actually setting the import-anchorto be test_A
, in other word, top-level package is test_A
. So, when we have test.py do from ..A import xxx
, you are escaping from the anchor, and Python does not allow this.
您实际上将导入锚点设置为test_A
,换句话说,顶级包为test_A
. 所以,当我们有 test.py do 时from ..A import xxx
,你正在逃离锚点,而 Python 不允许这样做。
[CASE 2] When you do
[案例 2] 当你这样做时
python -m package.test_A.test
or
或者
from package.test_A import test
your anchor becomes package
, so package/test_A/test.py
doing from ..A import xxx
does not escape the anchor(still inside package
folder), and Python happily accepts this.
你的锚点变成package
,所以package/test_A/test.py
这样做from ..A import xxx
并没有逃脱锚点(仍在package
文件夹中),Python 很高兴地接受了这一点。
In short:
简而言之:
- Absolute-import changes current anchor (=redefines what is the top-level package);
- Relative-import does not change the anchor but confines to it.
- 绝对导入更改当前锚点(=重新定义顶级包是什么);
- 相对导入不会改变锚点而是限制在它里面。
Furthermore, we can use full-qualified module name(FQMN) to inspect this problem.
此外,我们可以使用全限定模块名称(FQMN)来检查这个问题。
Check FQMN in each case:
在每种情况下检查 FQMN:
- [CASE2]
test.__name__
=package.test_A.test
- [CASE1]
test.__name__
=test_A.test
- [案例2]
test.__name__
=package.test_A.test
- [案例1]
test.__name__
=test_A.test
So, for CASE2, an from .. import xxx
will result in a new module with FQMN=package.xxx
, which is acceptable.
因此,对于 CASE2, anfrom .. import xxx
将产生一个带有 FQMN= 的新模块package.xxx
,这是可以接受的。
While for CASE1, the ..
from within from .. import xxx
will jump out of the starting node(anchor) of test_A
, and this is NOT allowed by Python.
而对于 CASE1,..
from insidefrom .. import xxx
会跳出 的起始节点(锚点)test_A
,这是 Python 不允许的。
回答by Andre de Miranda
Not sure in python 2.x but in python 3.6, assuming you are trying to run the whole suite, you just have to use -t
在 python 2.x 中不确定,但在 python 3.6 中,假设您正在尝试运行整个套件,您只需要使用 -t
-t, --top-level-directory directory Top level directory of project (defaults to start directory)
-t, --top-level-directory directory 项目的顶级目录(默认为启动目录)
So, on a structure like
所以,在像这样的结构上
project_root
|
|----- my_module
| \
| \_____ my_class.py
|
\ tests
\___ test_my_func.py
One could for example use:
例如,可以使用:
python3 unittest discover -s /full_path/project_root/tests -t /full_path/project_root/
python3 unittest discover -s /full_path/project_root/tests -t /full_path/project_root/
And still import the my_module.my_class
without major dramas.
并且仍然导入my_module.my_class
没有主要的戏剧。