这里似乎已经有很多关于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
的消息。导致此错误消息的原因是什么?
答案 0 :(得分:130)
编辑:在其他问题中,这个问题有更好/更一致的答案:
为什么它不起作用?这是因为python没有记录加载包的位置。因此,当您执行python -m test_A.test
时,它基本上只会放弃test_A.test
实际存储在package
中的知识(即package
不被视为包)。尝试from ..A import foo
试图访问它不再拥有的信息(即加载位置的兄弟目录)。它在概念上类似于在from ..os import path
中的文件中允许math
。这会很糟糕,因为你希望包是不同的。如果他们需要使用其他软件包中的内容,那么他们应该使用from os import path
在全局范围内引用它们,并让python在$PATH
和$PYTHONPATH
处找到它。
当您使用python -m package.test_A.test
时,使用from ..A import foo
就可以解决问题了,因为它会跟踪package
中的内容并且您只是访问子目录加载的位置。
为什么python不认为当前的工作目录是一个包? NO CLUE ,但是gosh它会很有用。
答案 1 :(得分:86)
import sys
sys.path.append("..") # Adds higher directory to python modules path.
试试这个。 为我工作。
答案 2 :(得分:34)
假设:
如果您位于package
目录中,则A
和test_A
是单独的包。
结论:
..A
导入仅允许在包中。
进一步说明:
如果要强制将包放在sys.path
上的任何路径上,那么仅在包中提供相对导入是有用的。
编辑:
我是唯一一个认为这是疯了的人!?为什么世界上当前的工作目录不被认为是一个包? - Multihunter
当前工作目录通常位于sys.path中。所以,那里的所有文件都是可导入的。这是自Python 2以来尚未存在的行为。使运行目录成为一个包将允许将模块导入为“import .A”和“import A”,然后将导入两个不同的模块。也许这是一个不一致的问题。
答案 3 :(得分:9)
from package.A import foo
我认为它比
更清晰import sys
sys.path.append("..")
答案 4 :(得分:7)
在3.6中,这些解决方案都不适用于我,其文件夹结构如下:
package1/
subpackage1/
module1.py
package2/
subpackage2/
module2.py
我的目标是从module1导入module2。终于对我有用的是,很奇怪:
import sys
sys.path.append(".")
请注意,与到目前为止提到的两点解决方案不同。
编辑:以下内容为我澄清了这一点:
import os
print (os.getcwd())
就我而言,工作目录(出乎意料地)是项目的根。
答案 5 :(得分:6)
这在Python中非常棘手。
我将首先说明您为什么会遇到此问题,然后我将提及两种可能的解决方案。
您必须考虑Python documentation中的这一段:
请注意,相对导入基于当前名称 模块。由于主模块的名称始终为“ 主要”, 旨在用作Python应用程序主模块的模块 必须始终使用绝对导入。
还有PEP 328中的以下内容:
相对导入使用模块的 name 属性来确定 模块在包层次结构中的位置。如果模块名称正确 不包含任何软件包信息(例如,它设置为“ 主要”) 然后解析相对导入,就好像该模块是顶级 模块,无论模块实际位于文件中的何处 系统。
相对导入从文件名(bool Person::lessThan(Person* per){
if(strcmp(this->name, per->name) < 0){
return true;
}else{
return false;
}
}
属性)开始,该文件名可以采用两个值:
__name__
Python在这里知道父目录:package.test_A.test
到test
之前,然后是test_A
。
因此您可以将点符号用于相对导入。 package
然后,您可以在根目录中拥有一个名为# package.test_A/test.py
from ..A import foo
的根文件:
test.py
# root.py
from package.test_A import test
)时,它成为程序的入口点,因此test.py
== __name__
。文件名没有指示目录结构,因此Python不知道如何进入目录。对于Python,__main__
成为顶级脚本,上面没有任何内容。这就是为什么您不能使用相对导入的原因。A)一种解决方法是在根目录中有一个调用模块/软件包的根文件,如下所示:
test.py
导入root.py
。 (进入点,test.py
)。__name__ == __main__
(相对)导入test.py
。foo.py
表示模块已导入。输出为:
foo.py
B)如果您想将代码作为模块而不是顶级脚本执行,可以从命令行尝试:
package.A.foo has been imported
Module's name is: package.test_A.test
欢迎任何建议。
您还应该检查Relative imports for the billionth time,特别是BrenBarn的答案。
答案 6 :(得分:4)
如果在提供了很好的答案后仍然有人在挣扎,请考虑检查以下内容:
上述网站的基本报价:
“可以通过这种方式以编程方式指定相同内容:
导入系统
sys.path.append('..')
当然,上面的代码必须在其他导入之前编写 声明。
很明显,必须这样,事后才思考。我试图在测试中使用sys.path.append('..'),但遇到了OP发布的问题。通过在其他导入之前添加import和sys.path定义,可以解决此问题。
答案 7 :(得分:4)
最流行的答案表明,基本上是因为您的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 :(得分:2)
如果上层文件夹中有__init__.py
,则可以将导入初始化为
该初始化文件中的import file/path as alias
。然后,您可以在较低的脚本中使用它,例如:
import alias
答案 9 :(得分:1)
以我的拙见,我以这种方式理解这个问题:
[情况1]当您开始像这样的绝对导入时
python -m test_A.test
或
import test_A.test
或
from test_A import test
您实际上是将 import-anchor 设置为test_A
,换句话说,顶级包是test_A
。因此,当我们执行test.py做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会欣然接受。
简而言之:
此外,我们可以使用full-qualified module name(FQMN)检查此问题。
在每种情况下检查FQMN:
test.__name__
= package.test_A.test
test.__name__
= test_A.test
因此,对于CASE2,from .. import xxx
将导致新模块的FQMN = {package.xxx
,这是可以接受的。
对于CASE1,..
中的from .. import xxx
将从test_A
的起始节点(锚点)跳出,而这是不允许的Python。
答案 10 :(得分:1)
不确定在python 2.x中,但在python 3.6中,假设您尝试运行整个套件,则只需使用-t
-t,--top-level-directory目录 项目的顶层目录(默认为起始目录)
因此,在类似
的结构上project_root
|
|----- my_module
| \
| \_____ 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
且没有大戏剧的情况下。
答案 11 :(得分:1)
拥有
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
答案 12 :(得分:0)
当我使用Django 2.1.3时,该命令对我不起作用:
import sys
sys.path.append("..") # Adds higher directory to python modules path.
我选择了一个自定义解决方案,在该解决方案中,我向服务器启动脚本添加了一个命令,以将共享脚本复制到需要共享python脚本的django“ app”中。 这不是理想的选择,但是由于我只是在开发一个个人网站,因此适合我。如果可以在单个网站内的Django Apps之间找到共享代码的django方式,我将再次在此处发布。
答案 13 :(得分:0)
就我而言,我不得不更改为: 解决方案1(更好,取决于当前的py文件路径。易于部署) 使用pathlib.Path.parents make code cleaner
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