为什么 Python 的 `from` 形式的 import 语句会绑定模块名称? [英] 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打印a",实用程序从 testapp.api.utils 导入 x打印b",实用程序
和 testapp/api/utils.py
定义了 x
:
x = 1
现在从根导入 testapp.api
:
$ export PYTHONPATH=$PYTHONPATH:.$ python -c "import testapp.api"来自testapp/utils.pyc"的<模块testapp.utils">b <来自'testapp/api/utils.pyc'的模块'testapp.api.utils'>
导入的结果让我吃惊,因为它表明第二个 import
语句覆盖了 utils
.然而文档声明 from 语句不会绑定模块名称:
from 表单不绑定模块名称:它遍历列表标识符,在步骤中找到的模块中查找每个标识符(1),并将本地命名空间中的名称绑定到对象,从而找到了.
事实上,当我在终端中使用 from ... import ...
语句时,没有引入模块名称:
我怀疑这与 Python 有关,在第二个 import 语句时,尝试导入 testapp.api.utils
引用 testapp.utils
和失败了,但我不确定.
这里发生了什么?
来自 导入系统文档:
<块引用>当使用任何机制(例如 importlib
API)加载子模块时,import
或 import-from
语句,或内置的 __import__()
)绑定放置在父模块的命名空间中到子模块目的.例如,如果包 spam
有一个子模块 foo
,则在导入 spam.foo
,spam
将有一个属性 foo
是绑定到子模块.假设您有以下目录结构:
垃圾邮件/__init__.py文件酒吧.py
和 spam/__init__.py
中有以下几行:
from .foo import Foo从 .bar 导入栏
然后执行以下将一个名称绑定到 foo
和 bar
中垃圾邮件
模块:
鉴于 Python 熟悉的名称绑定规则,这可能看起来令人惊讶,但它实际上是进口系统的一个基本特征.这不变的持有是,如果你有 sys.modules['spam']
和sys.modules['spam.foo']
(就像上面导入后一样),后者必须作为前者的 foo
属性出现.
如果你执行 from testapp.api.utils import x
,import 语句不会将 utils
加载到本地命名空间中.但是,导入机制将加载 utils
到 testapp.api
命名空间中,以使进一步的导入正常工作.碰巧在您的情况下,testapp.api
也是本地命名空间,因此您会感到惊讶.
I have a Python project with the following structure:
testapp/
├── __init__.py
├── api
│ ├── __init__.py
│ └── utils.py
└── utils.py
All of the modules are empty except testapp/api/__init__.py
which has the following code:
from testapp import utils
print "a", utils
from testapp.api.utils import x
print "b", utils
and testapp/api/utils.py
which defines x
:
x = 1
Now from the root I import 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'>
The result of the import surprises me, because it shows that the second import
statement has overwritten utils
. Yet the docs state that the 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.
And indeed, when in a terminal I use a from ... import ...
statement, no module names are introduced:
>>> from os.path import abspath
>>> path
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'path' is not defined
I suspect this has to do with Python, at the time of the second import statement, trying to import testapp.api.utils
which refers to testapp.utils
and failing but I'm not certain.
What is happening here?
From the 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.
If you do from testapp.api.utils import x
, the import statement will not load utils
into the local namespace. However, the import machinery will load utils
into the testapp.api
namespace, to make further imports work right. It just happens that in your case, testapp.api
is also the local namespace, so you're getting a surprise.
这篇关于为什么 Python 的 `from` 形式的 import 语句会绑定模块名称?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!