一尘不染

Python-相对导入十亿次

python

存在的问题是:在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

导入是从控制台完成的。

我确实在相应的模块中创建了名为垃圾邮件和鸡蛋的函数。自然,它不起作用。答案显然是在我列出的第4个网址中,但对我来说都是校友。我访问的其中一个URL上有此响应:

相对导入使用模块的名称属性来确定该模块在包层次结构中的位置。如果模块的名称不包含任何包信息(例如,将其设置为“ main”),则相对导入的解析就好像该模块是顶级模块一样,无论该模块实际位于文件系统上的哪个位置。

上面的回答看起来很有希望,但对我来说,全是象形文字。因此,我的问题是,如何使Python不返回“未打包的相对导入尝试”?可能有一个涉及-m的答案。

有人可以告诉我为什么Python会给出该错误消息,“非包装”是什么意思,为什么以及如何定义“包装”以及准确的答案


阅读 482

收藏
2020-02-06

共1个答案

一尘不染

脚本与模块

这是一个解释。简短的版本是直接运行Python文件与从其他位置导入该文件之间存在很大差异。 仅知道文件位于哪个目录并不能确定Python认为位于哪个软件包。 此外,这还取决于你如何通过运行或导入将文件加载到Python中。

加载Python文件的方式有两种:作为顶级脚本或作为模块。如果直接执行文件(例如,通过python myfile.py在命令行上键入),则会将文件作为顶级脚本加载。如果你这样做python -m myfile,则将其作为模块加载,或者import在其他文件中遇到语句时将其加载。一次只能有一个顶级脚本。顶层脚本是你为了开始而运行的Python文件。

命名

加载文件时,将为其指定一个名称(存储在其__name__属性中)。如果已将其作为顶级脚本加载,则其名称为__main__。如果将其作为模块加载,则其名称为文件名,其后是其所属的所有软件包/子软件包的名称,以点分隔。

例如,在你的示例中:

package/
    __init__.py
    subpackage1/
        __init__.py
        moduleX.py
    moduleA.py

如果你导入moduleX(请注意:imported,未直接执行),则其名称为package.subpackage1.moduleX。如果导入moduleA,则名称为package.moduleA。但是,如果直接从命令行运行 moduleX,则名称为__main__,如果直接从命令行运行moduleA,则名称为__main__。当模块作为顶级脚本运行时,它将失去其常规名称,而其名称改为__main__

不通过其包含的包访问模块

还有一个额外的问题:模块的名称取决于它是从其所在目录“直接”导入还是通过软件包导入。仅当你在目录中运行Python并尝试在该目录(或其子目录)中导入文件时,这才有所不同。例如,如果你在目录中启动Python解释器package/subpackage1,然后执行do import moduleX,则其名称moduleX将仅为moduleX,而不是package.subpackage1.moduleX。这是因为Python在启动时会将当前目录添加到其搜索路径中。如果它在当前目录中找到了要导入的模块,则不会知道该目录是软件包的一部分,并且软件包信息也不会成为模块名称的一部分。

一种特殊情况是,如果你以交互方式运行解释器(例如,只需键入python并开始即时输入Python代码)。在这种情况下,该交互式会话的名称为__main__

现在,这是你的错误消息的关键所在:如果模块的名称没有点,则不认为它是包的一部分。文件实际在磁盘上的哪个位置都没有关系。重要的是它的名称是什么,它的名称取决于你如何加载它。

现在查看你在问题中包含的报价:

相对导入使用模块的名称属性来确定该模块在包层次结构中的位置。如果模块的名称不包含任何包信息(例如,将其设置为“ main”),则相对导入的解析就好像该模块是顶级模块一样,无论该模块实际位于文件系统上的哪个位置。

相对进口…

相对导入使用模块的名称来确定它在包中的位置。当你使用类似的相对导入时from .. import foo,点表示在软件包层次结构中增加了一定数量的级别。例如,如果你当前模块的名称是package.subpackage1.moduleX,则..moduleA表示package.moduleA。为了使a from .. import起作用,模块名称必须至少包含与import语句中一样多的点。

…只是相对的

但是,如果模块的名称为__main__,则不认为它在软件包中。它的名称没有点,因此你不能from .. import在其中使用语句。如果你尝试这样做,则会收到“非包中的相对导入”错误。

脚本无法导入相对

你可能要做的是尝试从命令行运行moduleX等。当你执行此操作时,其名称设置为__main__,这意味着其中的相对导入将失败,因为其名称不会显示它在软件包中。请注意,如果你从模块所在的同一目录中运行Python,然后尝试导入该模块,也会发生这种情况,因为如上所述,Python会“过早”在当前目录中找到该模块,而没有意识到它是包装的一部分。

还请记住,当你运行交互式解释器时,该交互式会话的“名称”始终为__main__。因此,你不能直接从交互式会话进行相对导入。相对导入仅在模块文件中使用。

两种解决方案:

如果你确实确实想moduleX直接运行,但是仍然希望将其视为软件包的一部分,则可以这样做python -m package.subpackage1.moduleX。该命令-m告诉Python将其作为模块而不是顶级脚本进行加载。

也许你实际上并不想运行 moduleX,而只想运行其他脚本,例如myfile.py,该脚本使用 inside的功能moduleX。如果是这样的话,把myfile.py 其他地方 - 没有内部package目录-并运行它。如果myfile.py你在内部做类似的事情from package.moduleA import spam,它将很好地工作。

笔记

  • 对于这两种解决方案中的任何一种,都package必须可从Python模块搜索路径(sys.path)访问包目录(在你的示例中)。如果不是,你将根本无法可靠地使用包装中的任何物品。

  • 从Python 2.6开始,用于程序包解析的模块的“名称”不仅由其__name__属性确定,而且由__package__属性确定。这就是为什么我避免使用显式符号__name__来引用模块的“名称”的原因。因为Python 2.6模块的“名”是有效的__package__ + '.' + __name__,或者只是__name__如果__package__None)。

2020-02-06