在导入的__init__.py中,导入的行为会有所不同

DeT*_*ReR 12 python python-import python-2.7 python-internals

__init__.py运行文件时,导入时的行为似乎与导入时的行为不同.

如果我们有以下文件:

run.py:

import test
Run Code Online (Sandbox Code Playgroud)

test/b.py:

class B(object):
    pass
Run Code Online (Sandbox Code Playgroud)

test/__init__.py:

from b import B
print B
print b
Run Code Online (Sandbox Code Playgroud)

如果我们运行,__init__.py我们会得到一个错误:

% python test/__init__.py

<class 'b.B'>
Traceback (most recent call last):
  File "test/__init__.py", line 6, in <module>
  print b
NameError: name 'b' is not defined
Run Code Online (Sandbox Code Playgroud)

但如果我们run.py这样做,那么我们不会:

% python run.py 
<class 'test.b.B'>
<module 'test.b' from '~/temp/test/b.py'>
Run Code Online (Sandbox Code Playgroud)

我希望这种行为是一样的.为什么这样做?

这只有在我们这样做的情况下才有效__init__.py.要是我们:

mv __init__.py a.py
touch __init__.py
Run Code Online (Sandbox Code Playgroud)

并使run.py:

import test.a
Run Code Online (Sandbox Code Playgroud)

然后我们得到了错误.

And*_*ini 5

情况如下:你有一个脚本(run.py),一个包test及其子模块test.b.

每当您在Python中导入子模块时,该子模块的名称都会自动存储到父包中.因此,当您执行import collections.abc(或from collections.abc import Iterable类似)时,包会collections自动获取该属性abc.

这就是这里发生的事情.当你这样做时:

from b import B
Run Code Online (Sandbox Code Playgroud)

名称b是自动加载的test,因为btest包的子模块.

即使您没有import b明确地执行,每当您导入该模块时,名称都会被放入test.因为b是子模块test,它属于test.


侧节点:您的代码不适用于Python 3,因为相对导入已被删除.要使代码与Python 3一起使用,您必须编写:

from test.b import B
Run Code Online (Sandbox Code Playgroud)

这种语法完全相同from b import B,但更加明确,应该可以帮助您了解正在发生的事情.


参考:Python的参考文档也解释了这种行为,并包含一个有用的例子,非常类似于这种情况(所不同的是仅仅是一个绝对导入使用,而不是相对进口).

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

假设您有以下目录结构:

spam/
    __init__.py
    foo.py
    bar.py
Run Code Online (Sandbox Code Playgroud)

spam/__init__.py在其中包含以下内容:

from .foo import Foo
from .bar import Bar
Run Code Online (Sandbox Code Playgroud)

然后执行以下把将名称绑定到foobar所述在spam模块:

>>> import spam
>>> spam.foo
<module 'spam.foo' from '/tmp/imports/spam/foo.py'>
>>> spam.bar
<module 'spam.bar' from '/tmp/imports/spam/bar.py'>
Run Code Online (Sandbox Code Playgroud)

鉴于Python熟悉的名称绑定规则,这似乎令人惊讶,但它实际上是导入系统的基本功能.不变控股是,如果你有sys.modules['spam']sys.modules['spam.foo'](可能也会上述进口后),后者必须表现为foo前者的属性.