来自相对包

时间:2015-05-01 10:51:43

标签: python import

我偶然发现了一些奇怪的python(2.7)导入行为,虽然易于解决,但让我摸不着头脑。

给出以下文件夹结构:

test/
    __init__.py
    x.py
    package/
        __init__.py
        x.py

test / package / __ init__.py包含以下内容

from .. import x
print x
from .x import hello
print x
print x.hello

test / package / x.py包含以下内容

hello = 1

为什么在以下输出中从REPL结果运行import test.package

<module 'test.x' from 'test/x.pyc'>
<module 'test.package.x' from 'test/package/x.pyc'>
1

我原本希望x引用顶级x模块,但第二个导入代替的是导入整个本地x模块(不仅仅是{{1}正如我所预期的那样,有效地践踏了第一次导入。

有人可以在这里解释导入的机制吗?

1 个答案:

答案 0 :(得分:2)

T意识到from .x import name需要成为一个模块。然后检查test.package.x中的相应条目;如果在那里找到,则将sys.modules导入调用模块。

但是,如果sys.modules['test.package.x'].hello尚不存在,则加载模块;并且在加载sys.modules['test.package.x']的最后一步设置为指向新加载的模块,即使您明确没有要求它。因此,第二个导入会覆盖第一个导入的名称。

这是设计的,否则

sys.modules['test.package'].x

import foo.bar.baz
foo.bar.baz.x()

不可互换。

我无法在Python 2文档中找到关于此行为的良好文档,但Python 3 behaviour在这种情况下基本相同:

  

使用任何机制(例如from foo.bar import baz baz.x() API,importlibimport - import语句或内置from)加载子模块时绑定放在父模块的命名空间中,放在子模块对象中。例如,如果包__import__()具有子模块spam,则在导入foo后,spam.foo将具有绑定到子模块的属性spam

     

[...]

     

不变量持有是,如果您有foosys.modules['spam'](就像上面的导入后那样),后者必须显示为前者的sys.modules['spam.foo']属性。