ImportError: 无法从“__main__”中导入名称“settings”

13 浏览
0 Comments

ImportError: 无法从“__main__”中导入名称“settings”

我在这里访问过:

我还访问了许多其他的网址,有些是 SO 上的,有些是其他网站上的,当时我以为会很快解决问题。

一直反复出现的问题是:我该如何解决“Attempted relative import in non-package”这个错误信息呢?

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 的函数。当然,它没有起作用。答案似乎在我列出的第四个网址中,但对我而言它都像外语一样。我访问的其中一个 URL 上有这样的回答:

相对导入使用模块的名称属性来确定该模块在包层次中的位置。如果模块的名称不包含任何包信息(例如它被设置为\'main\'),那么相对导入将被解析为顶级模块,无论该模块实际上位于文件系统的何处。

以上回答看起来很有前途,但它对我来说就像是一种神秘的符号。所以我的问题是,如何让Python不给我返回\"尝试在非包中进行相对导入\"的错误信息?有一个涉及-m的答案。

能否有人告诉我为什么Python会给出这个错误信息,\"非包\"是什么意思,为什么和如何定义一个\'包\',以及明确的答案用简单易懂的方式解释给像幼儿园儿童一样的人听懂。

admin 更改状态以发布 2023年5月21日
0
0 Comments

这真的是 Python 中的一个问题。 混淆的起源是人们错误地把相对导入与路径相关联,而实际上它们并不相关。

例如,当你在 faa.py 内写这些代码时:

from .. import foo

这只有在 faa.py 被识别并且在执行时被加载为包的一部分时才有意义。在这种情况下,faa.py 的模块名称会是例如某个包名称.faa。如果文件只是因为它在当前目录下被加载,当 Python 运行时,它的名称将不会指向任何包,最终相对导入将失败。

引用当前目录中的模块的一个简单解决方案是使用这个:

if __package__ is None or __package__ == '':
    # uses current directory visibility
    import foo
else:
    # uses current package visibility
    from . import foo

0
0 Comments

脚本 vs. 模块

以下是一个解释。简而言之,直接运行一个 Python 文件与从其他地方导入该文件有很大的区别。只知道文件在哪个目录下并不能确定 Python 认为它属于哪个包。 这还取决于你如何将文件加载到 Python 中(通过运行或导入)。

加载 Python 文件有两种方式:作为顶级脚本或作为模块。如果你直接执行一个文件,例如在命令行上键入 python myfile.py,则它将作为顶级脚本加载。当文件内出现一个 import 语句时,则将其作为模块加载。同时只能有一个顶级脚本;顶级脚本是你运行以启动程序的 Python 文件。

命名

当一个文件被加载时,它被赋予一个名称(存储在其 __name__ 属性中)。

  • 如果它被作为顶级脚本加载,则其名称为 __main__
  • 如果它被作为模块加载,则其名称为[文件名,前面是它所属的任何包/子包的名称,用点分隔],例如package.subpackage1.moduleX

但要注意,如果你以类似 python -m package.subpackage1.moduleX 的方式从 shell 命令行加载 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并开始即兴输入Python代码),那么该交互式会话的名称就是__main__

现在这里有一个关键的信息有关于你的错误消息: 如果一个模块名没有点, 它就不被认为是一个包的一部分。 它实际上存储于磁盘上的位置并不重要。最重要的是它的名称和它的名称取决于你如何加载它。

现在看看你在你的问题中引用的内容:

相对导入使用模块的名称属性确定模块在包层次结构中的位置。如果模块的名称不包含任何包信息(例如设置为'main'), 相对导入将被解析为顶层模块, 不管模块实际上在文件系统上位于哪里。

相对导入...

相对导入使用模块的名称确定它在一个包中的位置。当你使用相对导入,如from.. import foo,点号表示向上跨越一定数量的层次在包层次结构中。例如,如果你当前的模块名是package.subpackage1.moduleX,那么..moduleA就意味着package.moduleA。对于from.. import语句,要工作,模块的名称必须至少有与import语句中的点数相同的点。

...仅在一个包中是相对的

然而,如果你的模块名是__main__,它不被认为是一个包。它的名字没有点,因此你不能在其内部使用from..import语句。如果你尝试这样做,你会得到“非包中的相对导入”错误。

脚本无法进行相对导入

你可能试着从命令行运行moduleX,或者类似的操作。在这种情况下,它的名称被设置为__main__,这意味着其中的相对导入会失败,因为它的名称没有揭示它是一个包的一部分。请注意,如果你从一个模块所在的目录运行Python,然后尝试导入该模块,也会发生类似状况,因为如上所述,Python会在当前目录中“过早”地找到该模块,而没有意识到它是包的一部分。

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

两种解决方法:

  1. 如果你确实想要直接运行moduleX,但你仍然希望它被视为一个包的一部分,那么可以使用python -m package.subpackage1.moduleX-m告诉 Python将其作为模块加载,而不是作为顶级脚本。

  2. 或者你实际上并不想运行moduleX,你只想运行一些其他的脚本,比如myfile.py,它使用了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__,如果__package__None,则只是__name__。)

0