我得到一个'模块对象没有属性......'尝试使用我创建的包层次结构时出错。 该错误让人想起循环导入时出现的错误(即模块 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'
这里有三个要点:
模块 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
中的 print 语句中。当解释器到达那里时,它所做的大致是这样的:
在系统模块中为 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 在回声符号中添加"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)
。这开始了这个序列:
- 获取全局变量
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。
此外,正如 @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
所有这些都已加载(或者更确切地说,由于它们已加载而阻止这样做(。这可能会导致您看到的问题。