【问题标题】:Why might Python's `from` form of an import statement bind a module name?为什么 Python 的 `from` 形式的 import 语句会绑定模块名称?
【发布时间】:2015-04-30 17:45:30
【问题描述】:

我有一个 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

和定义xtestapp/api/utils.py

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:

from 表单不绑定模块名称:它通过列表 标识符,在步骤中找到的模块中查找它们中的每一个 (1),并将本地命名空间中的名称绑定到对象,从而 找到了。

确实,当我在终端中使用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 有关,在第二个 import 语句时,尝试导入引用 testapp.utilstestapp.api.utils 并失败但我不确定。

这里发生了什么?

【问题讨论】:

  • 我没想到会出现这种行为,我也很想听到答案。
  • 你能从各种 utils 文件中添加一些代码吗?
  • @NikosM。正如我提到的,所有其他文件都是空的。
  • 是的,我明白了,你必须使用from module.name import property as alias 结构来避免命名空间冲突,因为本地命名空间对于你的 init 文件是相同的
  • 如果testapp/utils.py(以及testapp/api/__init__.py中的前两个非空行)被删除,这个例子会更清楚。它们对问题并不重要,只是分散注意力。

标签: python import


【解决方案1】:

来自import system documentation

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

spam/
    __init__.py
    foo.py
    bar.py

spam/__init__.py 中包含以下几行:

from .foo import Foo
from .bar import Bar

然后执行以下将名称绑定到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'>

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

如果您执行from testapp.api.utils import x,则导入语句不会将utils 加载到本地命名空间中。但是,导入机制utils 加载到testapp.api 命名空间中,以使进一步的导入正常工作。碰巧在您的情况下,testapp.api 也是本地命名空间,所以您会感到惊讶。

【讨论】:

  • 没有投反对票,但你能解释更多吗,我不清楚
  • 如果您使用from 形式,则不会将名称导入本地命名空间。你是说如果他们指的是某些东西,他们无论如何都会受到约束?如果这是真的,那么path = 1; from os.path import abspath; path 不应评估为1
  • @CeasarBautista:模块名称未绑定在 local 命名空间中,但它 绑定在包的命名空间中。只是在这种情况下,它们是相同的命名空间。
  • @CeasarBautista:添加了文档参考。我找不到 Python 2 版本,所以我链接了 Python 3 版本,但行为是一样的。
  • 重要的是要强调命名空间是相同的,因为这发生在__init__.py 内部。如果这是除此之外的任何模块,则不会发生此行为。
猜你喜欢
  • 2020-04-26
  • 1970-01-01
  • 2018-03-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-18
相关资源
最近更新 更多