我来过这里:
还有很多我没有复制的网址,有些在SO上,有些在其他网站上,当我认为我很快就有解决方案时 .
永远反复出现的问题是:使用Windows 7,32位Python 2.7.3,如何解决这个“非包装中尝试相对导入”的消息?我在pep-0328上构建了一个包的精确复制品:
package/
__init__.py
subpackage1/
__init__.py
moduleX.py
moduleY.py
subpackage2/
__init__.py
moduleZ.py
moduleA.py
我确实在适当的模块中创建了名为spam和eggs的函数 . 当然,它没有用 . 答案显然是我列出的第4个网址,但这对我来说都是校友 . 我访问过的其中一个网址上有此回复:
相对导入使用模块的name属性来确定模块在包层次结构中的位置 . 如果模块的名称不包含任何包信息(例如,它设置为'main'),则解析相对导入,就像模块是顶级模块一样,无论模块实际位于文件系统的哪个位置 .
上面的反应看起来很有希望,但它对我来说都是象形文字 . 所以我的问题是,我怎么让Python不回到我身边“试图在非包装中进行相对导入”?有一个答案涉及-m,据说 .
有人可以告诉我为什么Python会给出错误消息,非包装意味着什么!为什么以及如何定义'package'和 the precise answer put in terms easy enough for a kindergartener to understand .
编辑:导入是从控制台完成的 .
8 回答
这是一个我不推荐的解决方案,但在某些不生成模块的情况下可能会有用:
给PyPi写了一个小python包,可能会帮助这个问题的 Spectator . 如果希望能够在包/项目中运行包含包含上层包的导入的python文件而不直接在导入文件的目录中,则该包用作解决方法 . https://pypi.org/project/import-anywhere/
我有一个类似的问题,我不想改变Python模块搜索路径,并需要从脚本相对加载一个模块(尽管"scripts can't import relative with all"正如上面很好地解释了BrenBarn) .
所以我使用了以下hack . 不幸的是,它依赖于自版本3.4以来被弃用的
imp
模块,而不是importlib
. (这也可以用importlib
吗?我不知道 . )不过,黑客现在还可以使用 .从
subpackage2
文件夹中的脚本访问subpackage1
中的moduleX
成员的示例:更简洁的方法似乎是修改Federico提到的用于加载模块的sys.path .
这在python中确实是一个问题 . The origin of confusion is that people mistakenly takes the relative import as path relative which is not.
例如,当你用faa.py写时:
只有在执行期间由python识别并加载faa.y作为包的一部分时,这才有意义 . 在这种情况下,faa.py的 module's name 将是例如some_packagename.faa . 如果文件是因为它在当前目录中而加载的,那么当运行python时,它的名称将不会引用任何包,最终相对导入将失败 .
在当前目录中引用模块的简单解决方案是使用:
这里's a general recipe, modified to fit as an example, that I am using right now for dealing with Python libraries written as packages, that contain interdependent files, where I want to be able to test parts of them piecemeal. Let' s调用
lib.foo
并说它需要访问lib.fileA
用于函数f1
和f2
,并且lib.fileB
用于类Class3
.我已经包含了几个
print
调用来帮助说明这是如何工作的 . 在实践中,你会想要删除它们(也可能是from __future__ import print_function
行) .当我们确实需要在
sys.path
中插入一个条目时,这个特殊的例子太简单了 . (有关我们确实需要它的情况,请参阅Lars' answer,当我们有两个或更多级别的包目录时,然后我们使用os.path.dirname(os.path.dirname(__file__))
- 但它在这里也没有真正受到伤害 . )如果没有if _i in sys.path
这样做也很安全测试 . 但是,如果每个导入的文件都插入相同的路径 - 例如,如果fileA
和fileB
都想从包中导入实用程序 - 这会多次使用相同的路径混淆sys.path
,那么在样板文件中使用if _i not in sys.path
会很不错 .这里的想法是这样的(并注意这些在python2.7和python 3.x中的功能相同):
如果从普通代码作为常规包导入
import lib
或from lib import foo
运行,__package
是lib
,__name__
是lib.foo
. 我们采用第一个代码路径,从.fileA
导入,等等 .如果以
python lib/foo.py
运行,__package__
将为无,__name__
将为__main__
.我们采用第二个代码路径 .
lib
目录已经存在在sys.path
所以没有必要添加它 . 我们从fileA
等进口如果在
lib
目录中以python foo.py
运行,则行为与案例2相同 .如果在
lib
目录中以python -m foo
运行,则行为类似于情况2和3.但是,lib
目录的路径不在sys.path
中,因此我们在导入之前添加它 . 如果我们运行Python然后import foo
,则同样适用 .(由于
.
在sys.path
中,我们实际上并不需要在这里添加路径的绝对版本 . 这是一个更深层的包嵌套结构,我们想做的事情from ..otherlib.fileC import ...
,有所不同 . 如果你不这样做,你可以完全省略所有的sys.path
操作 . )注意事项
还有一个怪癖 . 如果你从外面运行这整件事:
要么:
行为取决于
lib/__init__.py
的内容 . 如果存在并且是空的,一切都很好:但是如果
lib/__init__.py
本身导入routine
以便它可以直接导出routine.name
为lib.name
,那么你得到:也就是说,模块导入两次,一次通过包,然后再次导入为
__main__
,以便它运行main
代码 . Python 3.6及更高版本警告:警告是新的,但警告的行为不是 . 这是有些人称之为the double import trap的一部分 . (有关其他详细信息,请参阅issue 27487 . )Nick Coghlan说:
请注意,虽然我们在此处违反了该规则,但只有当正在加载的文件未作为程序包的一部分加载时才会执行此操作,并且我们的修改专门用于允许我们访问该程序包中的其他文件 . (并且,正如我所指出的,我们可能根本不应该对单级包进行此操作 . )如果我们想要更加清洁,我们可能会将其重写为例如:
也就是说,我们修改
sys.path
足够长的时间来实现我们的导入,然后将其恢复原样(当且仅当我们添加了_i
的一个副本时才删除_i
的一个副本) .Script vs. Module
这是一个解释 . 简短的版本是直接运行Python文件和从其他地方导入该文件之间存在很大差异 . Just knowing what directory a file is in does not determine what package Python thinks it is in. 另外,这还取决于如何将文件加载到Python中(通过运行或导入) .
加载Python文件有两种方法:作为顶级脚本或作为模块 . 如果直接执行文件,则会将文件作为顶级脚本加载,例如在命令行上键入
python myfile.py
. 如果执行python -m myfile
,则将其作为模块加载,或者在其他文件中遇到import
语句时加载它 . 一次只能有一个顶级脚本;顶级脚本是您运行的Python文件 .Naming
加载文件时,会为其指定一个名称(存储在
__name__
属性中) . 如果它作为顶级脚本加载,则其名称为__main__
. 如果它作为模块加载,则其名称是文件名,前面是作为其一部分的任何包/子包的名称,以点分隔 .例如,在您的示例中:
如果导入
moduleX
(注意:导入,不直接执行),其名称将为package.subpackage1.moduleX
. 如果导入moduleA
,则其名称为package.moduleA
. 但是,如果直接从命令行运行moduleX
,则其名称将改为__main__
,如果直接从命令行运行moduleA
,则其名称将为__main__
. 当模块作为顶级脚本运行时,它将丢失其正常名称,而其名称将改为__main__
.Accessing a module NOT through its containing package
还有一个问题:模块的名称取决于它是从它所在的目录导入"directly"还是通过包导入 . 如果您在目录中运行Python,并尝试导入同一目录(或其子目录)中的文件,这只会有所不同 . 例如,如果您在目录
package/subpackage1
中启动Python解释器然后执行import moduleX
,moduleX
的名称将只是moduleX
,而不是package.subpackage1.moduleX
. 这是因为Python在启动时将当前目录添加到其搜索路径中;如果它在当前目录中找到要导入的模块,它将不知道该目录是包的一部分,并且包信息不会成为模块名称的一部分 .一个特例是如果您以交互方式运行解释器(例如,只需输入
python
并开始即时输入Python代码) . 在这种情况下,该交互式会话的名称是__main__
.现在,这是您的错误消息的关键所在: if a module's name has no dots, it is not considered to be part of a package . 文件在磁盘上的实际位置并不重要 . 重要的是它的名称,它的名称取决于你如何加载它 .
现在看看你在问题中包含的引用:
Relative imports...
相对导入使用模块的名称来确定它在包中的位置 . 当您使用
from .. import foo
之类的相对导入时,这些点表示在包层次结构中增加一些级别 . 例如,如果您当前模块的名称是package.subpackage1.moduleX
,则..moduleA
将表示package.moduleA
. 要使from .. import
工作,模块的名称必须至少与import
语句中的点一样多 .... are only relative in a package
但是,如果您的模块名称为
__main__
,则不会将其视为包中 . 它的名字没有点,因此你不能在里面使用from .. import
语句 . 如果您尝试这样做,您将收到"relative-import in non-package"错误 .Scripts can't import relative
你可能做的是你试图从命令行运行
moduleX
或类似的东西 . 执行此操作时,其名称设置为__main__
,这意味着其中的相对导入将失败,因为其名称不会显示它在包中 . 请注意,如果您从模块所在的同一目录运行Python,然后尝试导入该模块,也会发生这种情况,因为如上所述,Python会在当前目录_382689中找到该模块而不会意识到它是一个模块的一部分包 .还要记住,当您运行交互式解释器时,该交互式会话的"name"始终为
__main__
. 因此 you cannot do relative imports directly from an interactive session . 相对导入仅用于模块文件中 .Two solutions:
如果您确实想直接运行
moduleX
,但仍希望将其视为包的一部分,则可以执行python -m package.subpackage1.moduleX
.-m
告诉Python将其作为模块加载,而不是作为顶级脚本加载 .或许你实际上并不想运行
moduleX
,你只想运行一些其他脚本,例如myfile.py
,它使用moduleX
中的函数 . 如果是这种情况,请将myfile.py
放在其他地方 - 不在package
目录中 - 并运行它 . 如果在myfile.py
内你做from package.moduleA import spam
这样的事情,它会正常工作 .Notes
对于这些解决方案中的任何一个,必须可以从Python模块搜索路径(
sys.path
)访问包目录(在您的示例中为package
) . 如果不是,您根本无法可靠地使用包装中的任何东西 .从Python 2.6开始,用于包解析目的的模块"name"不仅取决于
__name__
属性,还取决于__package__
属性 . 's why I' m避免使用显式符号__name__
来引用模块's 382712 . Since Python 2.6 a module' s "name"实际上是__package__ + '.' + __name__
,如果__package__
是None
则只是__name__
. )所以在与其他许多人一起解决这个问题之后,我在article中发现了Dorian B发布的一条说明,解决了我在开发用于Web服务的模块和类的具体问题,但我也希望能够使用PyCharm中的调试工具在我编码时测试它们 . 要在自包含类中运行测试,我将在类文件的末尾包含以下内容:
但是如果我想在同一个文件夹中导入其他类或模块,那么我必须将所有的import语句从相对符号更改为本地引用(即删除点( . ))但是在阅读了Dorian的建议之后,我尝试了他的'单线'并且它有效!我现在可以在PyCharm中进行测试,并在我在另一个被测试的类中使用该类时,或者当我在我的Web服务中使用它时,保留我的测试代码!
if语句检查我们是否将此模块作为 main 运行,或者它是否's being used in another module that'被测试为 main . 也许这是显而易见的,但我在这里提供这个注释,以防其他人因上述相对导入问题而感到沮丧,可以利用它 .
__name__
的更改取决于相关代码是在全局命名空间中运行还是作为导入模块的一部分运行 .如果代码未在全局空间中运行,则
__name__
将是模块的名称 . 如果它在全局命名空间中运行 - 例如,如果您将其键入控制台,或使用python.exe yourscriptnamehere.py
将该模块作为脚本运行,则__name__
将变为"__main__"
.您将看到许多带有
if __name__ == '__main__'
的python代码用于测试代码是否从全局命名空间运行 - 这允许您拥有一个兼作脚本的模块 .您是否尝试从控制台进行这些导入?