【发布时间】:2015-12-27 19:02:27
【问题描述】:
这是在今天回答 another question 时出现的。
假设以下文件,其中 cmets 表示文件名:
# level1/__init__.py
import level1.level2
answer = level1.level2.answer
# level1/level2/__init__.py
from .b import answer
# level1/level2/b.py
from .a import answer
from ..level2.a import answer
from level1.level2.a import answer
import level1.level2.a
if answer != 42:
answer = level1.level2.a.answer # <-- Fails here
#level1/level2/a.py
answer = 42
使用此代码,python -c "import level1" 可以正常工作(在 2.7 和 3.4 中)。但是将答案更改为 42 以外的任何内容,它都会在指定位置出现 AttributeError: 'module' object has no attribute 'level2' 失败。
似乎 from/import 可以在所有父命名空间设置之前导入包的子模块并将变量拉出其命名空间,但是(显然)必须在 sub 的属性之前设置父命名空间-命名空间可以通过普通的属性访问来遍历。
不过,有时,命名空间设置得“足够好”以供导入工作。例如,下面的代码,去掉了顶层,总是与python -c "import level2"一起工作,即使当我们从level2.bimport level2.a时我们还没有完成初始化level2命名空间。
# level2/__init__.py
from .b import answer
# level2/b.py
from .a import answer
import level2.a
if answer != 42:
answer = level2.a.answer # <-- Works here
#level1/level2/a.py
answer = 41
编辑
import x.y.z 似乎会将对 z 的引用插入到 y 中,但不会将对 y 的引用插入到 x 中。例如,当我从第一个示例中更改 level1/level2/b.py 时:
# level1/level2/b.py
from sys import modules
def dump():
print '\n Dumping...'
for key, value in sorted(modules.items()):
if key.startswith('level'):
print key, [ x for x in dir(value) if not x.startswith('_')]
dump()
import level1.level2.a
dump()
from .a import answer
from ..level2.a import answer
from level1.level2.a import answer
if answer != 42:
answer = level1.level2.a.answer
我在回溯之前得到以下结果:
Dumping...
level1 []
level1.level2 []
level1.level2.b ['dump', 'modules']
level1.level2.sys []
Dumping...
level1 []
level1.level2 ['a']
level1.level2.a ['answer']
level1.level2.b ['dump', 'level1', 'modules']
level1.level2.level1 []
level1.level2.sys []
但是,如果在第二次调用 dump() 之后,我添加了以下行:
setattr(modules['level1'], 'level2', modules['level1.level2'])
那么它不会失败,因为在通过属性查找访问它之前,我已经将level2 绑定到level1。但是,如果解释器可以在该导入时将a 绑定到level1,它可能也可以以完全相同的方式将level2 绑定到level。
在 import 语句期间只更新最低级别的包是否有充分的理由,或者它是一个错误(或者更确切地说,可能是为单个包添加的一个特性,应该扩展到嵌套包)?
注意根据 Python 文档,When a submodule is loaded using any mechanism ... a binding is placed in the parent module’s namespace to the submodule object.。
我相信这会发生。但在所有情况下都不够快。此外,它的文档可能是relatively new。
【问题讨论】:
-
您能否更具体地说明“在指定位置出现导入错误失败”?我知道它在
import期间失败了(这就是我们正在做的一切),但回溯说AttributeError: 'module' object has no attribute 'level2',这是真的:它没有。我猜“当你导入父级时,你不能自动访问子包”是我简单的英文解释。在后者中,您正在访问一个模块,而不是试图进入另一个包。我可以参考语法更正式地写出来,但我觉得我错过了一些东西,所以先评论...... -
@JRichardSnape -- 对不起,你说得对。正在考虑 AttributeError 并编写 ImportError。现在逐字复制。 但是... 这并没有直接向我解释为什么我在第二个示例中没有遇到同样的问题——在这种情况下,为什么
a可以从level2中退出在我们完成构建level2之前? FWIW,我认为这不能用语法来解释——我认为这两种情况基本相同——但我当然可能是错的。 -
嗯 - 我会想更多。我假设你已经通过了语法(并且,作为一个推论,我错过了一些东西)并且你现在已经按照我接下来要测试的思路编辑了一些不错的调试结果,所以需要弄清楚我的想法围绕他们。
-
@JRichardSnape -- 还有一件事。如果在第二次转储之后,我添加了
setattr(modules['level1'], 'level2', modules['level1.level2'])行,那么它工作正常。这似乎是 import 语句可以自行设置的东西。 -
有趣。当它工作时,在整个事情之后做一个
dump()(即answer == 42)。也很有趣……
标签: python