gpt4 book ai didi

Python 条件 'module object has no attribute' 错误,个​​人包不同于循环导入问题

转载 作者:太空狗 更新时间:2023-10-29 17:31:17 25 4
gpt4 key购买 nike

我在尝试使用我创建的包层次结构时收到“模块对象没有属性...”错误。该错误让人想起循环导入时出现的错误(即模块 a 导入 b 和模块 b导入 a),但我在这里看不到那个问题。我浏览了很多有类似错误的帖子,但没有一个我认为非常合适的解释。

这是在 python 2.7.1 和 python 2.4.3 中看到的。

我已将其淡化为以下示例:

考虑以下层次结构(见下面的代码):

alpha
alpha/__init__.py
alpha/bravo
alpha/bravo/__init__.py
alpha/bravo/charlie.py
alpha/bravo/delta.py
alpha/bravo/echo.py

模块 charlie 导入 echo,后者又导入 delta。如果 alpha/bravo/__init__.py(如 alpha/__init__.py)本质上是空白的,脚本可以:
import alpha.bravo.charlie

如果我尝试在 alpha/bravo/__init__.py 中导入 alpha.bravo.charlie,问题就会浮出水面(我认为我可以在那里显示相关的类/方法,并且脚本会执行“导入 alpha.bravo”)。

代码:

阿尔法/__init__.py
(blank)

阿尔法/布拉沃/__init__.py
import alpha.bravo.charlie

阿尔法/布拉沃/charlie.py
import alpha.bravo.echo
def charlie_foo(x): return str(x)
def charlie_bar(x): return alpha.bravo.echo.echo_biz()

阿尔法/布拉沃/delta.py
def delta_foo(x): return str(x)

阿尔法/布拉沃/echo.py
import alpha.bravo.delta
print alpha.bravo.delta.delta_foo(1)
def echo_biz(): return 'blah'

如果我尝试:
python -c 'import alpha.bravo'

我得到:
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "/home/kmkc980/svn/working/azcif/python/lib/alpha/bravo/__init__.py", line 1, in <module>
import alpha.bravo.charlie
File "/home/kmkc980/svn/working/azcif/python/lib/alpha/bravo/charlie.py", line 1, in <module>
import alpha.bravo.echo
File "/home/kmkc980/svn/working/azcif/python/lib/alpha/bravo/echo.py", line 2, in <module>
print alpha.bravo.delta.delta_foo(1)
AttributeError: 'module' object has no attribute 'bravo'

但是,如果我注释掉 alpha/bravo/__init__.py 中的导入行,那么一切似乎都可以:
python -c 'import alpha.bravo'

python -c 'import alpha.bravo.charlie'
1

此外,如果我使用上面相同的代码(包括 alpha/bravo/__init__.py 中的导入行),但编辑所有内容以排除层次结构的“alpha”级别,它似乎工作正常。

所以层次结构现在只是:
bravo
bravo/__init__.py
bravo/charlie.py
bravo/delta.py
bravo/echo.py

我将所有带有“alpha.bravo.*”的行更改为“bravo.*”

那么没问题:
python -c 'import bravo'
1

我已经能够解决这个问题,但我仍然想了解它。谢谢。

最佳答案

这就是为什么

(我相信,这主要得到 http://docs.python.org/faq/programming.html#how-can-i-have-modules-that-mutually-import-each-other 中的解释的支持)

当 Python 解释器遇到形式为 import a.b.c 的一行时,它通过以下步骤运行。在伪 python 中:

for module in ['a', 'a.b', 'a.b.c']:
if module not in sys.modules:
sys.modules[module] = (A new empty module object)
run every line of code in module # this may recursively call import
add the module to its parent's namespace
return module 'a'

这里有三个重要的点:
  • 如果模块 a、a.b 和 a.b.c 尚未导入,它们将按顺序导入
  • 模块在完全导入完成之前不存在于其父级的命名空间中。所以模块a没有 b属性直到 a.b已经完全进口。
  • 不管你的模块链有多深,即使你import a.b.c.d.e.f.g ,您的代码只会在其命名空间中添加一个符号:a .

    因此,当您稍后尝试运行 a.b.c.d.e.f.g.some_function() 时,解释器必须一直遍历模块链才能到达该方法。

  • 这是发生了什么

    根据您发布的代码,问题似乎出在 alpha/bravo/echo/__init__.py 中的打印语句中。 .解释器到达那里时所做的事情大致如下:
  • 在 sys.modules
  • 中为 alpha 设置一个空模块对象
  • 运行 alpha/__init__.py 中的代码(注意 dir(alpha) 此时不包含 'bravo')
  • 在 sys.modules 中为 alpha.bravo 设置一个空模块对象
  • 运行 alpha/bravo/__init__.py 中的代码:

    4.1 在sys.modules中为alpha.bravo.charlie设置一个空的模块对象

    4.2 运行 alpha/bravo/charlie/__init__.py 中的代码:

    4.2.1 在sys.modules中为alpha/bravo/echo设置一个空的模块对象

    4.2.2 运行 alpha/bravo/echo/__init__.py 中的代码:

    4.2.2.1 在sys.modules中为alpha/bravo/delta设置一个空的模块对象

    4.2.2.2 运行 alpha/bravo/delta/__init__.py 中的代码 -- 到此结束,所以 'delta' 被添加到 'alpha.bravo' 的符号中。

    4.2.2.3 给echo 的符号添加'alpha'。这是import alpha.bravo.delta的最后一步.

  • 此时,如果我们对 sys.modules 中的所有模块调用 dir() ,我们将看到:
  • “阿尔法”:['__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__', '__return__'] (这基本上是空的)
  • 'alpha.bravo':['__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__', 'delta'] (delta 已经导入完毕,所以它在这里)
  • 'alpha.bravo.charlie':['__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__'] (空)
  • 'alpha.bravo.delta':['__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__', '__return__', 'delta.foo'] (这是唯一完成的)
  • 'alpha.bravo.echo':['__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__', '__return__', 'alpha']

  • 现在解释器继续使用 alpha/bravo/echo/__init__.py,它遇到了行 print alpha.bravo.delta.delta_foo(1) .这开始了这个序列:
  • 获取全局变量 alpha -- 这将返回仍然为空的 alpha模块。
  • 调用 getattr(alpha, 'bravo') -- 这失败了,因为 alpha.bravo尚未完成初始化,因此 bravo 尚未插入到 alpha 的符号表中。

  • 这与循环导入期间发生的事情相同——模块没有完成初始化,所以符号表没有完全更新,属性访问失败。

    如果你用这个替换 echo/__init__.py 中的违规行:
    import sys
    sys.modules['alpha.bravo.delta'].delta_foo(1)

    这可能会奏效,因为 delta 已完全初始化。但是在 bravo 完成之前(在 echo 和 charlie 返回之后),alpha 的符号表不会更新,您将无法通过它访问 bravo。

    另外,正如@Ric Poggi 所说,如果您将导入行更改为
    from alpha.bravo.delta import delta_foo

    那么这将起作用。在这种情况下,因为 from alpha.bravo.delta直接转到 sys.modules dict,而不是从 alpha 到 bravo 再到 delta,它可以从 delta 模块中获取函数并将其分配给一个局部变量,然后您可以轻松访问该变量。

    关于Python 条件 'module object has no attribute' 错误,个​​人包不同于循环导入问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8991520/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com