PyCharm 在相对导入中显示错误,即使它不是错误。
PyCharm 在相对导入中显示错误,即使它不是错误。
我已经访问过以下网站:
- http://www.python.org/dev/peps/pep-0328/
- http://docs.python.org/2/tutorial/modules.html#packages
- Python packages: relative imports
- python relative import example code does not work
- Relative imports in python 2.5
- Relative imports in Python
- Python: Disabling relative import
还有很多我没有复制的URL,一些在SO上,一些在其他网站上,当我认为我会很快获得解决方案时。
永恒反复的问题是:我如何解决这个“尝试在非包中进行相对导入”的消息?
ImportError: attempted relative import with no known parent package
我制作了pep-0328包的精确副本:
package/ __init__.py subpackage1/ __init__.py moduleX.py moduleY.py subpackage2/ __init__.py moduleZ.py moduleA.py
导入是从控制台完成的。
我确实在它们各自的模块中创建了名为spam和eggs的函数。当然,它没起作用。答案显然在我列出的第4个URL中,但对我来说这一切都是外人。我访问的其中一个网址上有这样的回答:
相对导入使用模块的名称属性来确定该模块在包层次结构中的位置。如果模块的名称不包含任何包信息(例如设置为\'main\'),则相对导入将被解析为顶级模块,而不管模块实际位于文件系统的位置。
上面的回复看起来很有前途,但对我来说,这全是象形文字。那么我的问题是,如何使Python不将“Attempted relative import in non-package”返回给我,它的答案涉及-m,据说是这样。
有人能告诉我为什么Python会出现这个错误消息,它所谓的“非包”是什么意思,为什么和如何定义一个\'包\',以及用幼儿易懂的术语准确回答这个问题。
这是Python中的一个实际问题。混淆的根源在于人们错误地将相对导入视为路径相对导入,但它实际上并不是。
例如,当您在faa.py中编写以下内容时:
from .. import foo
只有当faa.py在执行期间作为包的一部分被Python识别和加载时,这才有意义。在这种情况下,faa.py的模块名称将是例如some_packagename.faa。如果文件仅因为它位于当前目录中而被Python运行,则其名称将不引用任何包,最终相对导入将失败。
引用当前目录中的模块的一个简单解决方案是使用此方法:
if __package__ is None or __package__ == '': # uses current directory visibility import foo else: # uses current package visibility from . import foo
脚本 vs. 模块
这里有一个解释。简单来说,直接运行 Python 文件和从其他地方导入该文件之间有很大的区别。知道一个文件在哪个目录并不能确定 Python 认为它在哪个包中。这还要另外取决于你将文件加载到 Python 中的方式(运行或导入)。
加载 Python 文件有两种方式:作为顶层脚本或作为模块。如果您直接执行它,例如在命令行上键入python myfile.py
,则文件将作为顶层脚本加载。如果在其他文件中遇到import
语句,则会将其作为模块加载。一次只能有一个顶层脚本;顶层脚本是您运行以启动项目的 Python 文件。
命名
加载文件时,它会被赋予一个名称(存储在其__name__
属性中)。
- 如果加载为顶层脚本,则其名称为
__main__
。 - 如果加载为模块,则其名称为[文件名,加上它所属的任何包/子包的名称,用点隔开],例如
package.subpackage1.moduleX
。
但要注意,如果您从 shell 命令行以模块的方式加载moduleX
,例如使用python -m package.subpackage1.moduleX
,__name__
仍然会是__main__
。
因此,举个例子:
package/ __init__.py subpackage1/ __init__.py moduleX.py moduleA.py
如果你导入了moduleX
(注意:只是导入,而不是直接执行),它的名称将会是package.subpackage1.moduleX
。如果你导入了moduleA
,它的名称将会是package.moduleA
。然而,如果你直接从命令行运行moduleX
,它的名称将会是__main__
,而如果你直接从命令行运行moduleA
,它的名称将会是__main__
。当一个模块被作为顶层脚本运行时,它失去了它的正常名称,而其名称变成了__main__
。
不通过包来访问一个模块
还有一个额外的问题:模块的名称取决于是否直接从所在目录导入,还是通过包导入。这只有在你在一个目录中运行Python,并试图导入该目录中的文件(或其子目录中的文件)时才有影响。例如,如果你在目录package/subpackage1
中启动Python解释器,然后执行import moduleX
,那么moduleX
的名称将只是moduleX
,而不是package.subpackage1.moduleX
。这是因为Python在交互式进入解释器时会将当前目录添加到它的搜索路径中;如果它在当前目录中找到要导入的模块,它不会知道该目录是包的一部分,而该包信息将不会成为模块名称的一部分。
一个特殊情况是如果你交互地运行解释器(例如,只是输入python
并开始动态输入Python代码),那么该交互会话的名称是__main__
。
现在关键的事情是你的错误消息:如果一个模块的名称没有点,它就不被认为是包的一部分。它实际上在磁盘上的位置无所谓,它的名称就是要考虑的问题,而它的名称取决于你如何加载它。
现在看一下你在问题中包含的引用:
相对导入使用模块的名称属性来确定该模块在包层次结构中的位置。如果模块的名称不包含任何包信息(例如它被设置为'main')那么相对导入将解析该模块,就好像该模块是一个顶级模块,而不管该模块实际上在文件系统上的位置。
相对导入...
相对导入使用模块的名称来确定它在包中的位置。当你使用一个相对导入,比如from .. import foo
,点表示在包层次结构中向上移动一定数量的级别。例如,如果你当前的模块名称是package.subpackage1.moduleX
,那么..moduleA
将意味着package.moduleA
。为了让from .. import
生效,模块名称必须至少包含与import
语句中相同数量的点。
...只有在一个包中才是相对的
然而,如果你的模块名称是__main__
,它就不被认为是在一个包中。它的名称没有点,因此你不能在它里面使用from .. import
语句。如果你试图这样做,你将得到“在非包中使用相对导入”错误。
脚本无法导入相对路径
你可能尝试从命令行中运行moduleX
或类似的脚本。这时,它的名称被设置为__main__
,这意味着在其内部进行的相对导入将会失败,因为其名称没有显示它是在一个包中。请注意,如果你从与模块相同的目录中运行Python,并尝试导入该模块,也会发生这种情况,因为正如上文所述,Python将在当前目录中“过早”地找到该模块,而不意识到它是包的一部分。
还要记住,当你运行交互式解释器时,该交互式会话的“名称”始终为__main__
。因此,你不能直接从交互式会话中进行相对导入。相对导入仅用于模块文件内。
两种解决方法:
-
如果你真的想直接运行
moduleX
,但仍希望它被视为一个包的一部分,你可以使用python -m package.subpackage1.moduleX
运行它。-m
告诉Python将其作为一个模块加载,而不是作为顶级脚本。 -
或者,也许你实际上并不想运行
moduleX
,你只想运行一些其他脚本,比如myfile.py
,它使用moduleX
中的函数。如果是这种情况,请将myfile.py
放在别处-不要放在package
目录中-然后运行它。如果在myfile.py
中做一些像from package.moduleA import spam
的事情,它将可以正常工作。
li>
ol>
注意事项 strong> p>
-
对于这两种解决方案,包目录(在您的示例中为
包 code>)必须从Python模块搜索路径(
sys.path code>)可访问。 如果不是这样,您将无法可靠地使用包中的任何内容。
li>
-
自Python 2.6以来,模块的用于解决包的“名称”不仅由其
__name__ code>属性确定,而且还由
__package__ code>属性确定。 这就是为什么我想避免使用显式符号
__name__ code>引用模块的“名称”。 自Python 2.6以来,模块的“名称”有效地为
__package__ + '.' + __name__ code>,如果
__package__ code>为
None code>,则仅为
__name__ code>。) p>
li>
ul>