为什么 Python 的 `from` 导入语句形式会绑定模块名称?
Why might Python's `from` form of an import statement bind a module name?
我有一个 Python 项目,其结构如下:
testapp/
├── __init__.py
├── api
│ ├── __init__.py
│ └── utils.py
└── utils.py
除具有以下代码的 testapp/api/__init__.py
外,所有模块都是空的:
from testapp import utils
print "a", utils
from testapp.api.utils import x
print "b", utils
和 testapp/api/utils.py
定义 x
:
x = 1
现在我从根导入 testapp.api
:
$ export PYTHONPATH=$PYTHONPATH:.
$ python -c "import testapp.api"
a <module 'testapp.utils' from 'testapp/utils.pyc'>
b <module 'testapp.api.utils' from 'testapp/api/utils.pyc'>
导入的结果让我很吃惊,因为它显示第二个import
语句已经覆盖了utils
。然而文档指出 from statement will not bind a module name:
The from form does not bind the module name: it goes through the list
of identifiers, looks each one of them up in the module found in step
(1), and binds the name in the local namespace to the object thus
found.
事实上,当我在终端中使用 from ... import ...
语句时,没有引入模块名称:
>>> from os.path import abspath
>>> path
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'path' is not defined
我怀疑这与 Python 有关,在第二个导入语句时,尝试导入 testapp.api.utils
引用 testapp.utils
但失败了,但我没有确定。
这里发生了什么?
来自import system documentation:
When a submodule is loaded using any mechanism (e.g. importlib
APIs,
the import
or import-from
statements, or built-in __import__()
)
a binding is placed in the parent module’s namespace to the submodule
object. For example, if package spam
has a submodule foo
, after
importing spam.foo
, spam
will have an attribute foo
which is
bound to the submodule. Let’s say you have the following directory
structure:
spam/
__init__.py
foo.py
bar.py
and spam/__init__.py
has the following lines in it:
from .foo import Foo
from .bar import Bar
then executing the following puts a name binding to foo
and bar
in
the spam
module:
>>> import spam
>>> spam.foo
<module 'spam.foo' from '/tmp/imports/spam/foo.py'>
>>> spam.bar
<module 'spam.bar' from '/tmp/imports/spam/bar.py'>
Given Python’s familiar name binding rules this might seem surprising,
but it’s actually a fundamental feature of the import system. The
invariant holding is that if you have sys.modules['spam']
and
sys.modules['spam.foo']
(as you would after the above import), the
latter must appear as the foo
attribute of the former.
如果您执行 from testapp.api.utils import x
,import 语句不会将 utils
加载到本地名称空间中。但是,导入机制 将 将 utils
加载到 testapp.api
命名空间中,以使进一步的导入工作正常。碰巧在你的情况下, testapp.api
也是本地名称空间,所以你会感到惊讶。
我有一个 Python 项目,其结构如下:
testapp/
├── __init__.py
├── api
│ ├── __init__.py
│ └── utils.py
└── utils.py
除具有以下代码的 testapp/api/__init__.py
外,所有模块都是空的:
from testapp import utils
print "a", utils
from testapp.api.utils import x
print "b", utils
和 testapp/api/utils.py
定义 x
:
x = 1
现在我从根导入 testapp.api
:
$ export PYTHONPATH=$PYTHONPATH:.
$ python -c "import testapp.api"
a <module 'testapp.utils' from 'testapp/utils.pyc'>
b <module 'testapp.api.utils' from 'testapp/api/utils.pyc'>
导入的结果让我很吃惊,因为它显示第二个import
语句已经覆盖了utils
。然而文档指出 from statement will not bind a module name:
The from form does not bind the module name: it goes through the list of identifiers, looks each one of them up in the module found in step (1), and binds the name in the local namespace to the object thus found.
事实上,当我在终端中使用 from ... import ...
语句时,没有引入模块名称:
>>> from os.path import abspath
>>> path
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'path' is not defined
我怀疑这与 Python 有关,在第二个导入语句时,尝试导入 testapp.api.utils
引用 testapp.utils
但失败了,但我没有确定。
这里发生了什么?
来自import system documentation:
When a submodule is loaded using any mechanism (e.g.
importlib
APIs, theimport
orimport-from
statements, or built-in__import__()
) a binding is placed in the parent module’s namespace to the submodule object. For example, if packagespam
has a submodulefoo
, after importingspam.foo
,spam
will have an attributefoo
which is bound to the submodule. Let’s say you have the following directory structure:spam/ __init__.py foo.py bar.py
and
spam/__init__.py
has the following lines in it:from .foo import Foo from .bar import Bar
then executing the following puts a name binding to
foo
andbar
in thespam
module:>>> import spam >>> spam.foo <module 'spam.foo' from '/tmp/imports/spam/foo.py'> >>> spam.bar <module 'spam.bar' from '/tmp/imports/spam/bar.py'>
Given Python’s familiar name binding rules this might seem surprising, but it’s actually a fundamental feature of the import system. The invariant holding is that if you have
sys.modules['spam']
andsys.modules['spam.foo']
(as you would after the above import), the latter must appear as thefoo
attribute of the former.
如果您执行 from testapp.api.utils import x
,import 语句不会将 utils
加载到本地名称空间中。但是,导入机制 将 将 utils
加载到 testapp.api
命名空间中,以使进一步的导入工作正常。碰巧在你的情况下, testapp.api
也是本地名称空间,所以你会感到惊讶。