Python条件下“模块对象没有属性”错误与个人包区别于循环导入问题
我在使用自己创建的包结构时,遇到了“模块对象没有这个属性...”的错误。这个错误让我想起了循环导入的问题(比如模块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,而echo又导入了delta。如果alpha/bravo/__init__.py(类似于alpha/__init__.py)基本上是空的,脚本可以这样做:
import alpha.bravo.charlie
如果我尝试在alpha/bravo/__init__.py中导入alpha.bravo.charlie(想着可以在这里暴露相关的类和方法,然后脚本可以执行'import alpha.bravo'),问题就出现了。
代码:
alpha/__init__.py
(blank)
alpha/bravo/__init__.py
import alpha.bravo.charlie
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()
alpha/bravo/delta.py
def delta_foo(x): return str(x)
alpha/bravo/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
我已经找到了解决这个问题的方法,但我还是想理解一下它的原因。谢谢。
2 个回答
与其使用绝对导入,不如试试相对导入。
比如说:
alpha/bravo/_init_.py
import alpha.bravo.charlie
应该是
import charlie
否则,很可能会出现循环导入的问题。也就是说,如果你在charlie里面导入alpha.bravo.charlie,这就意味着
alpha/__init__.py
bravo/__init__.py
charlie/__init__.py
所有的模块都会被加载(或者说,因为它们已经加载过,所以被阻止再次加载)。这可能就是你遇到的问题所在。
这是为什么
(我认为,这主要是通过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
在a.b
完全导入之前,不会有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 将'alpha'添加到echo的符号中。这是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']
现在解释器继续执行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字典,而不是从alpha到bravo再到delta进行遍历,它可以从delta模块获取函数并将其分配给一个局部变量,这样你就可以毫无问题地访问它。