【问题标题】:"ImportError: cannot import name ..." - raised on "import from" but not on direct import“ImportError: cannot import name ...” - 在“import from”时引发,但不是在直接导入时引发
【发布时间】:2012-09-15 10:37:45
【问题描述】:

Python 3 解释器引发此异常的确切规则是什么?

有很多关于此的 SO 问题,有很好的答案,但我找不到一个对发生此异常的情况给出清晰、一般和逻辑上精确的定义。

文档似乎也不清楚。它说:

异常ImportError

当导入语句找不到时引发 模块定义或from ... import 找不到名称时 这是要导入的。

但这似乎与以下示例不一致。

我的意思是要求一个一般定义而不是具体案例,但为了澄清我的担忧,这里有一个例子:

# code/t.py:
from code import d

# code/d.py
from code import t

从命令行运行模块t.py会产生ImportError: cannot import name d

另一方面,以下代码不会引发异常:

# code/t.py:
import code.d

# code/d.py
import code.t

在任何时候,__init__.py 都是空的。

在这个例子中,import语句中提到的唯一模块或名称是td,它们都被清楚地找到了。如果文档暗示在d 模块中 找不到某个名称,那肯定不是很明显;最重要的是,我希望它会引发NameError: name ... is not defined 异常而不是ImportError

【问题讨论】:

  • “有时这段代码:..导致ImportError异常”?因此,有时它有效,有时则无效。两者有什么不同?
  • @martineau: reworded.. 没有什么不同,两种情况下的代码相同,除了这一行。

标签: python import python-3.x importerror


【解决方案1】:

如果abc是一个包而xyz是一个模块,并且如果abc__init__.py定义了一个不包含__all____all__,那么你将无法做到from abc import xyz,但您仍然可以使用 import abc.xyz

编辑:简短的回答是:您的问题是您的进口是循环的。模块 t 和 d 尝试相互导入。这行不通。不要这样做。我将在下面解释整个事情,但解释很长。

要了解它为什么会出现 ImportError,请尝试跟踪代码执行。如果您查看完整的回溯,而不仅仅是最后一部分,您可以看到它在做什么。通过您的设置,我得到了这样的回溯(我将包称为“testpack”而不是“code”):

Traceback (most recent call last):
  File "t.py", line 1, in <module>
    from testpack import d
  File "C:\Documents and Settings\BrenBarn\My Documents\Python\testpack\d.py", line 1, in <module>
    from testpack import t
  File "C:\Documents and Settings\BrenBarn\My Documents\Python\testpack\t.py", line 1, in <module>
    from testpack import d
ImportError: cannot import name d

您可以在这里看到 Python 正在做什么。

  1. 在加载t.py时,首先看到的是from testpack import d
  2. 此时,Python 会执行 d.py 文件来加载该模块。
  3. 但它首先找到的是from testpack import t
  4. 它已经加载了一次t.py,但是作为主脚本的t与作为模块的t不同,所以它尝试再次加载t.py
  5. 它首先看到的是from testpack import d,这意味着它应该尝试加载d.py。 . .但它已经 尝试在第 2 步中重新加载 d.py。由于尝试导入 d 导致再次尝试导入 d,Python 意识到它无法导入 d并抛出 ImportError。

第 4 步在这里有点反常,因为您直接在包中运行了一个文件,这不是通常的处理方式。请参阅this question,了解为什么导入模块与直接运行模块不同。如果您尝试导入 t(使用from testpack import t),Python 会提前一步实现循环,并且您会获得更简单的回溯:

>>> from testpack import t
Traceback (most recent call last):
  File "<pyshell#1>", line 1, in <module>
    from testpack import t
  File "C:\Documents and Settings\BrenBarn\My Documents\Python\testpack\t.py", line 1, in <module>
    from testpack import d
  File "C:\Documents and Settings\BrenBarn\My Documents\Python\testpack\d.py", line 1, in <module>
    from testpack import t
ImportError: cannot import name t

注意这里的错误是它不能导入t。它知道它不能,因为当我告诉它导入 t 时,它发现自己再次循环返回导入 t。在您的原始示例中,它没有注意到它运行了 t.py 两次,因为第一次是主脚本,第二次是导入,所以它又走了一步并尝试再次导入 d。

现在,为什么当您执行import code.d 时不会发生这种情况?答案只是因为你实际上并没有尝试使用导入的模块在这种情况下,它发生如下(我将解释为好像你做了from code import t而不是运行它脚本):

  1. 它开始导入 t。执行此操作时,它会临时将模块 code.t 标记为已导入,即使尚未完成导入。
  2. 它发现它必须做import code.d,所以它运行d。
  3. 在 d 中找到 import code.t,但由于 code.t 已被标记为已导入,因此不再尝试再次导入。
  4. 由于 d 没有实际使用 t 就完成了,它可以返回并完成加载 t。没问题。

主要区别在于名称td 在此处不能直接相互访问;它们由包code 调解,因此在实际使用之前,Python 实际上不必完成“确定 t 是什么”。对于from code import t,由于必须将值分配给变量t,Python 必须立即知道它是什么。

你可以看到问题,但如果你让d.py看起来像这样:

import code.t
print code.t

现在,在第 2 步之后,在运行 d 时,它实际上尝试访问半导入的模块 t。这将引发 AttributeError,因为模块尚未完全导入,因此尚未附加到包 code

请注意,只要在 d 完成运行之后才使用 code.t 就可以了。这将在d.py 中正常工作:

import code.t
def f():
    print code.t

您可以稍后致电f,它会起作用。原因是直到d执行完毕后才需要使用code.t,而d执行完毕后,可以返回执行t。

重申一下,这个故事的主要寓意是不要使用循环导入。它会导致各种头痛。相反,将公共代码分解到两个模块都导入的第三个模块中。

【讨论】:

  • 谢谢,很高兴知道!但就我而言,所有__init__.py 文件都是空的。如果有帮助,我可以复制导致问题的确切代码,但由于我正在寻找一般规则而不是特定问题的解决方案,我不确定它是否相关。
  • @max:如果你能提供一个简单的例子,那肯定会有所帮助。
  • @max:我编辑了一个简短的答案和一个长的解释。简短的回答是:问题在于您的导入是循环的。
【解决方案2】:

问题是abc 是一个预定义的标准库模块,只是创建一个具有__init__.py 的同名子目录并不会改变这一事实。通过将__init__.py 文件所在的文件夹重命名为不同的名称,即def,将包的名称更改为其他名称,然后import 的两种形式都应该正确执行。

【讨论】:

  • 很抱歉,这对我来说是一个糟糕的例子。我实际上并没有使用abc,我只是在我的问题中使用了它。我会记得永远不要在实际代码中使用它:)
【解决方案3】:
from abc import xyz

相当于做

xyz = __import__('abc').xyz

由于如果您只是 import abc,则如果没有单独的导入,abc.xyz 将不存在(除非 abc/__init__.py 包含针对 xyz 的显式 import),因此您看到的是预期行为。

【讨论】:

  • 但在第二种情况下(不引发异常的情况),我正在做import abc.xyz 而不是import abc
  • @max abc.xyz 是一个独立于 abc 的模块,导入一个模块不会隐式导入另一个模块。
猜你喜欢
  • 2022-10-15
  • 2020-07-07
  • 2019-07-20
  • 2020-07-16
  • 2019-06-10
  • 2014-09-25
  • 2014-01-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多