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



我得到一个'模块对象没有属性......'尝试使用我创建的包层次结构时出错。 该错误让人想起循环导入时出现的错误(即模块 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

模块查理导入回声,而回声又导入三角洲。 如果 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

阿尔法/布拉沃/查理.py

import alpha.bravo.echo
def charlie_foo(x): return str(x)
def charlie_bar(x): return alpha.bravo.echo.echo_biz()

阿尔法/布拉沃/三角洲.py

def delta_foo(x): return str(x)

阿尔法/布拉沃/回声.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 的行时,它会运行以下步骤。在伪蟒蛇中:

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'

这里有三个要点:

  1. 模块 a、a.b 和 a.b.c 将按顺序导入(如果尚未导入(

  2. 模块在完全完成导入之前不存在于其父级的命名空间中。因此,模块a在完全导入之前没有b属性a.b

  3. 无论你的模块链有多深,即使你import a.b.c.d.e.f.g你的代码也只会将一个符号添加到其命名空间中:a

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

这是正在发生的事情

根据您发布的代码,问题似乎出在 alpha/bravo/echo/__init__.py 中的 print 语句中。当解释器到达那里时,它所做的大致是这样的:

  1. 在系统模块中为 alpha 设置一个空模块对象

  2. 在 alpha/__init__.py 中运行代码(请注意,dir(alpha( 此时不会包含"bravo"(

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

  4. 在 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 在回声符号中添加"alpha"。这是import alpha.bravo.delta的最后一步。

此时,如果我们在 sys.modules 中的所有模块上调用 dir((,我们将看到以下内容:

    '
  • alpha':['__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']

现在,intepreter继续使用alpha/bravo/echo/__init__.py,在那里它遇到了线print alpha.bravo.delta.delta_foo(1)。这开始了这个序列:

  1. 获取全局变量 alpha -- 这将返回仍然为空的 alpha 模块。
  2. 调用 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。

此外,正如 @Rik Poggi 所说,如果您将导入行更改为

from alpha.bravo.delta import delta_foo

然后这将起作用。在这种情况下,由于from alpha.bravo.delta直接进入 sys.modules dict,而不是从 alpha 遍历到 bravo 再到 delta,它可以从 delta 模块获取函数并将其分配给局部变量,然后您可以毫无问题地访问该变量。

与其使用绝对导入,不如使用相对导入。

alpha/bravo/_init_.py

import alpha.bravo.charlie

应该是

import charlie

也就是说,如果你从查理那里导入alpha.bravo.charlie,这意味着

alpha/__init__.py
bravo/__init__.py
charlie/__init__.py 

所有这些都已加载(或者更确切地说,由于它们已加载而阻止这样做(。这可能会导致您看到的问题。

最新更新