she*_*per 238 python import package
看来这里已经有一些关于python 3中相对导入的问题了,但是在经历了很多这些问题后,我仍然找不到我的问题的答案.所以这是问题所在.
我有一个如下所示的包裹
package/
__init__.py
A/
__init__.py
foo.py
test_A/
__init__.py
test.py
Run Code Online (Sandbox Code Playgroud)
我在test.py中有一行:
from ..A import foo
Run Code Online (Sandbox Code Playgroud)
现在,我在文件夹中package,我跑了
python -m test_A.test
Run Code Online (Sandbox Code Playgroud)
我收到了消息
"ValueError: attempted relative import beyond top-level package"
Run Code Online (Sandbox Code Playgroud)
但如果我在父文件夹中package,例如,我运行:
cd ..
python -m package.test_A.test
Run Code Online (Sandbox Code Playgroud)
一切都好.
现在我的问题是:
当我在文件夹中时package,我在test_A子包中运行模块test_A.test,根据我的理解,..A只上升一个级别,它仍然在package文件夹中,为什么它给出了消息说beyond top-level package.导致此错误消息的原因是什么?
Mul*_*ter 151
这个问题在这个问题中有一个更连贯的答案:兄弟姐妹包导入
为什么不起作用?这是因为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不认为当前的工作目录是一个包? 没有CLUE,但天哪它会有用.
小智 122
import sys
sys.path.append("..") # Adds higher directory to python modules path.
Run Code Online (Sandbox Code Playgroud)
试试这个.为我工作.
Use*_*ser 41
假设:
如果您在package目录中,A并且test_A是单独的包.
结论:
..A只允许在包中进口.
附加说明:
如果要强制将包放在位于其上的任何路径上,则仅在包中提供相对导入非常有用sys.path.
编辑:
我是唯一一个认为这是疯了的人!?为什么世界上当前的工作目录不被认为是一个包?- 多猎人
当前工作目录通常位于sys.path中.所以,那里的所有文件都是可导入的.这是自Python 2以来尚未存在的行为.使运行目录成为一个包将允许将模块导入为"import .A"和"import A",然后将导入两个不同的模块.也许这是一个不一致的考虑因素.
Guz*_*ero 25
这在 Python 中非常棘手。
我将首先评论为什么您会遇到这个问题,然后我会提到两种可能的解决方案。
您必须考虑 Python文档中的这一段:
请注意,相对导入基于当前模块的名称。由于主模块的名称始终是“ main ”,因此用作 Python 应用程序主模块的模块必须始终使用绝对导入。
还有来自PEP 328的以下内容:
相对导入使用模块的名称属性来确定该模块在包层次结构中的位置。如果模块的名称不包含任何包信息(例如,它被设置为“ main ”),则相对导入将被解析为好像该模块是顶级模块,而不管该模块实际位于文件系统上的哪个位置。
相对导入从文件名(__name__属性)开始工作,它可以采用两个值:
package.test_A.test
这里Python知道的父目录:之前test说到test_A,然后package。
因此,您可以使用点表示法进行相对导入。# package.test_A/test.py
from ..A import foo
Run Code Online (Sandbox Code Playgroud)
然后你可以在根目录中有一个根文件,它调用test.py:
# root.py
from package.test_A import test
Run Code Online (Sandbox Code Playgroud)
test.py直接运行模块 ( ) 时,它成为程序的入口点,因此__name__== __main__。文件名没有说明目录结构,因此 Python 不知道如何在目录中上移。对于 Python 来说,test.py成为顶级脚本,在它之上没有任何东西。这就是您不能使用相对导入的原因。A)解决此问题的一种方法是拥有一个调用模块/包的根文件(在根目录中),如下所示:
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
Run Code Online (Sandbox Code Playgroud)
B)如果您想将代码作为模块而不是顶级脚本来执行,您可以从命令行尝试:
python -m package.test_A.test
Run Code Online (Sandbox Code Playgroud)
欢迎任何建议。
您还应该检查:第 10 亿次的相对进口,特别是 BrenBarn 的回答。
Jas*_*row 20
在3.6中,这些解决方案都不适用于我,其文件夹结构如下:
package1/
subpackage1/
module1.py
package2/
subpackage2/
module2.py
Run Code Online (Sandbox Code Playgroud)
我的目标是从module1导入module2。最终对我有用的是:
import sys
sys.path.append(".")
Run Code Online (Sandbox Code Playgroud)
请注意,单点与到目前为止提到的两点解决方案不同。
编辑:以下帮助为我澄清了这一点:
import os
print (os.getcwd())
Run Code Online (Sandbox Code Playgroud)
就我而言,工作目录(出乎意料地)是项目的根目录。
Chr*_*ett 13
这实际上比其他答案所描述的要简单得多。
TL;DR:直接导入A而不是尝试相对导入。
当前工作目录不是包,除非package您从其他文件夹导入该文件夹。因此,如果您打算由其他应用程序导入您的包,它的行为将会正常工作。不起作用的是测试......
在不更改目录结构中的任何内容的情况下,需要更改的只是test.py导入foo.py.
from A import foo
Run Code Online (Sandbox Code Playgroud)
现在python -m test_A.test从package目录运行将在没有ImportError.
您当前的工作目录不是包,但它已添加到路径中。因此,您可以A直接导入文件夹及其内容。这与您可以导入已安装的任何其他软件包的原因相同......它们都包含在您的路径中。
Joe*_*how 11
from package.A import foo
我认为它比清楚的更清楚
import sys
sys.path.append("..")
Run Code Online (Sandbox Code Playgroud)
如果您__init__.py在上层文件夹中有一个,则可以像import file/path as alias在该 init 文件中一样初始化导入
。然后你可以在较低的脚本上使用它:
import alias
Run Code Online (Sandbox Code Playgroud)
正如最流行的答案所暗示的,基本上是因为您的PYTHONPATH或sys.path包括.但不包括您通往包裹的路径。相对导入是相对于当前工作目录的,而不是相对于导入发生的文件的位置。奇怪。
您可以通过以下方法解决此问题:首先将相对导入更改为绝对导入,然后以以下内容开头:
PYTHONPATH=/path/to/package python -m test_A.test
Run Code Online (Sandbox Code Playgroud)
或以这种方式调用时强制使用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())
Run Code Online (Sandbox Code Playgroud)
小智 6
只需在 test.py 中删除..对我来说 pytest 可以很好地使用该
示例:
from A import foo
Run Code Online (Sandbox Code Playgroud)
如果在提供了不错的答案后仍然有人在挣扎,请考虑检查以下内容:
以上网站的基本报价:
“可以通过这种方式以编程方式指定相同的内容:
导入系统
sys.path.append('..')
当然,以上代码必须在其他import 语句之前编写。
很明显,必须这样,事后才思考。我试图在测试中使用sys.path.append('..'),但遇到了OP发布的问题。通过在其他导入之前添加import和sys.path定义,我可以解决此问题。
就我而言,我必须更改为:解决方案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
Run Code Online (Sandbox Code Playgroud)
解决方案2
import sys
import os
sys.path.append(os.getcwd())
from utils import MultiFileAllowed
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
184867 次 |
| 最近记录: |