当前位置: 首页 > 知识库问答 >
问题:

机制-为什么在具有相对导入的main上直接运行会导致“尝试在没有已知父包的情况下进行相对导入”

甘英光
2023-03-14

运行python main。py其中为main。py具有相对导入将失败。

<root>
└── src                            
   ├── main.py    # from main.py, want to use the stuff in other.py
   └── other.py
$ python main.py
Traceback (most recent call last):
  File "main.py", line 2, in <module>
    from . other import use_me
ImportError: attempted relative import with no known parent package
  • 梅因。派克
# 'import ...' is for absolute import only
# For relative import, must be 'from ... import ...'
# See https://www.python.org/dev/peps/pep-0328/#guido-s-decision
from . other import use_me
if __name__ == "__main__":
    use_me()
  • other.py
def use_me():
    print("thanks") 

PEP 338——以脚本形式执行模块很明显,显式相对导入无法从主模块中工作。

2.5b1的发布显示了这个PEP和PEP 328之间令人惊讶的(尽管回想起来很明显)相互作用——显式的相对导入不适用于主模块。这是因为相对导入依赖于__name__来确定当前模块在包层次结构中的位置。在主模块中,__name__的值总是'__main__',因此显式的相对导入总是失败的(因为它们只适用于包内的模块)。

原因解释如下,解决方法是更新__path__

  • Python Bug Tracker Issue1510172:绝对/相对导入不起作用

这个问题实际上并不是-m开关所独有的。问题在于,相对导入是基于\uuuuu name\uuuuu,并且在主模块中,\uuuuuu name\uuuuuu始终具有值\uuuuu main\uuuu。因此,相对导入目前无法从应用程序的主模块正常工作,因为主模块不知道它在Python模块名称空间中的实际位置(对于通过-m开关执行的主要模块,这至少在理论上是可以修复的,但是直接执行的文件和交互式解释器完全不走运)。

通过在模块顶部粘贴以下内容(在执行任何相对导入之前),您应该能够获得类似于旧的隐式相对导入行为的内容:这使相对导入机制将主模块视为一个包。这种解决方法的问题在于,与从主模块隐式相对导入的旧情况一样,您可能最终在sys中得到两个不同的同级模块副本。模块。一份副本将是\uuuuu main\uuuu。“foo”而另一个将是“package”。foo'(对于隐式相对导入,第一个副本应该是名为“foo”的顶级模块)。

if __name__ = '__main__':
   from os.path import dirname
   __path__ = [dirname(__file__)]
   del dirname
  • PEP 328——导入:多行和绝对/相对

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

PEP 366似乎已经启用了作为模块执行main.py的解决方案。

main.py可以通过:python-m执行

  • PEP 366--主模块显式相对导入

该PEP提出了一种向后兼容的机制,允许使用包内可执行模块的显式相对导入。由于PEP 328和PEP 338之间的尴尬互动,此类导入目前失败

通过添加新的模块级属性,如果使用-m开关执行模块,此PEP允许相关导入自动工作。当文件按名称执行时,模块本身中的少量样板文件将允许相关导入工作。

提议的主要变化是引入一个新的模块级属性,包。当它存在时,相对导入将基于该属性而不是模块名称属性。

与当前名称属性一样,设置包将由用于导入模块的PEP 302加载器负责。使用imp.new_module()创建模块对象的加载器将自动将新属性设置为无。当导入系统在没有包集(或设置为无)的模块中遇到显式相对导入时,它将计算并存储正常模块的正确值(name.rpartition ('.')[0]和包初始化模块的名称)。如果已经设置了包,那么导入系统将优先使用它来从名称和路径属性中重新计算包名称。

阅读了1510172和PEP 366之后,仍然无法准确理解错误发生的机制。

我假设有一个模块加载器,它使用\uuuuuu name\uuuuu来表示模块层次结构。它看起来是以

<root>
└── src                            
   ├── main.py    # from main.py, want to use the stuff in other.py
   └── other.py

这些\uuuuu name\uuuuuuuu\uuuuuu path\uuuuuu\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu包,模块加载器之间是如何关联的,以及引擎盖下到底发生了?


共有1个答案

田阳泽
2023-03-14

在Python 3中的相对导入中找到了答案。

注意:问题18018中的补丁添加了另一个if块,它将在上面的代码之前执行:

if (PyUnicode_CompareWithASCIIString(package, "") == 0) {
    PyErr_SetString(PyExc_ImportError,
            "attempted relative import with no known parent package");
    goto error;
}

它现在位于cpython/blob/master/Python/imc.

    last_dot = PyUnicode_GET_LENGTH(package);
    if (last_dot == 0) {
        goto no_parent_error;
    }

所以我理解的是,如果\uuu name\uuuuu设置为'\uuu main\uuuuuu'且没有点,它只会转到“尝试相对导入,但没有已知的父包”

答案还说明:

我们可以使用-m命令行选项运行该模块,该选项将“搜索sys.path命名的模块,并作为__main__模块*”执行其内容...-m完成所有导入自动设置__package__,但你可以自己做

 类似资料:
  • 我正在学习fastapi。我有这样一个非常简单的项目结构 内部

  • 这条线出错了。 尝试导入没有已知父包的相对导入 此错误是什么以及如何解决此错误?

  • 我正在学习使用python编程,并且我在从包中的模块导入时遇到问题。我正在使用Python 3.8.2 64位的Visual Studio代码。 我的项目目录 在电子商务/产品中。py我拥有的文件: 这样我就可以从电子商务/数据库导入数据库类。py文件。但我有个错误

  • 因此,我的文件/文件夹结构如下: 在内部,我试图使用以下语法导入:。 我使用的是VSCode,当我键入模块路径时,它甚至会自动完成。但是当我运行文件时,标题中出现了错误。 我在这里读了几十个关于堆栈溢出的答案,但是没有一个使用这样的例子。

  • 我正在学习用python编程,我在从包中的模块导入时遇到了问题。我使用的是Visual studio代码和Python 3.8.2 64位。 我的项目目录 在<code>电子商务/产品中。py文件我有: 这样我就可以从< code > ecommerce/Database . py 文件中导入< code>Database类。但是我出错了

  • 根据一些答案,我试图更具体一些。我想在我的main中导入打印、模型和代码。派克 我知道这个问题被问了很多,但是我仍然不知道我的代码有什么问题!我有一个这样的项目目录 我想导入从print.py和*从请求因此,我试图添加这些行在main.py 所有这些行都会导致相同的导入,即在没有已知父项的情况下尝试相对导入, 使用Python 39有人知道问题出在哪里吗?我很困惑,这似乎不起作用,在旧版本中可能吗