为什么 Python 的 `from` 形式的 import 语句会绑定模块名称? [英] Why might Python's `from` form of an import statement bind a module name?

查看:21
本文介绍了为什么 Python 的 `from` 形式的 import 语句会绑定模块名称?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个具有以下结构的 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 ... 语句时,没有引入模块名称:

<预><代码>>>>从 os.path 导入 abspath>>>小路回溯(最近一次调用最后一次):文件<stdin>",第 1 行,在 <module> 中NameError:未定义名称路径"

我怀疑这与 Python 有关,在第二个 import 语句时,尝试导入 testapp.api.utils 引用 testapp.utils 和失败了,但我不确定.

这里发生了什么?

解决方案

来自 导入系统文档:

<块引用>

当使用任何机制(例如 importlib API)加载子模块时,importimport-from 语句,或内置的 __import__())绑定放置在父模块的命名空间中到子模块目的.例如,如果包 spam 有一个子模块 foo,则在导入 spam.foospam 将有一个属性 foo 是绑定到子模块.假设您有以下目录结构:

垃圾邮件/__init__.py文件酒吧.py

spam/__init__.py 中有以下几行:

from .foo import Foo从 .bar 导入栏

然后执行以下将一个名称绑定到 foobar垃圾邮件模块:

<预><代码>>>>导入垃圾邮件>>>垃圾邮件<来自'/tmp/imports/spam/foo.py'的模块'spam.foo'>>>>垃圾邮件栏<来自'/tmp/imports/spam/bar.py'的模块'spam.bar'>

鉴于 Python 熟悉的名称绑定规则,这可能看起来令人惊讶,但它实际上是进口系统的一个基本特征.这不变的持有是,如果你有 sys.modules['spam']sys.modules['spam.foo'](就像上面导入后一样),后者必须作为前者的 foo 属性出现.

如果你执行 from testapp.api.utils import x,import 语句不会将 utils 加载到本地命名空间中.但是,导入机制加载 utilstestapp.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, 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.

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆