【问题标题】:from <module> import ... in __init__.py makes module name visible?from <module> import ... in __init__.py 使模块名称可见?
【发布时间】:2011-05-18 17:40:12
【问题描述】:

以下面的代码为例:

文件package1/__init__.py

from moduleB import foo
print moduleB.__name__

文件package1/moduleB.py

def foo(): pass

然后从当前目录:

>>> import package1
package1.moduleB

此代码在 CPython 中工作。令我惊讶的是__init__.py 语句中的from ... import 使moduleB 名称可见。根据Python documentation,不应该是这样的:

from表单不绑定模块名

有人能解释一下为什么 CPython 会这样工作吗?是否有任何文档详细描述了这一点?

【问题讨论】:

  • 你确定之前没有import moduleB吗?
  • 是的,我确定。这是可以重现情况的完整文件。
  • 我试过了,它适用于 Python2.7 但不适用于 Python3.2 ...
  • @JBernardo 在 Python 3 下使用from .moduleB import foo 可以得到同样的效果。

标签: python python-import


【解决方案1】:

该文档误导了您,因为它描述了从包含它的父包之外导入模块的更常见情况。

例如,在我自己的代码中使用“from example import submodule”,其中“example”是一些完全未连接到我自己的代码的第三方库,不绑定名称“example”。它仍然导入both example/__init__.py 和 example/submodule.py 模块,创建两个模块对象,并将 example.submodule 分配给第二个模块对象。

但是,来自子模块的名称的“from..import”必须在父包对象上设置子模块属性。考虑一下是否没有:

  1. package/__init__.py 在包被导入时执行。

  2. __init__ 执行“从子模块导入名称”。

  3. 稍后,其他完全不同的代码会执行“import package.submodule”。

在第 3 步,sys.modules["package.submodule"] 不存在,在这种情况下再次加载它会给你两个不同范围内的 不同 模块对象;或 sys.modules["package.submodule"] 将存在,但 "submodule" 不会是父包对象 (sys.modules["package"]) 的属性,并且 "import package.submodule" 将什么也不做。但是,如果它什么都不做,那么使用 import 的代码就无法将子模块作为包的属性访问!


理论上,如果将导入机制的其余部分更改为匹配,则可以更改导入子模块的工作方式。

如果您只需要知道从包 P 导入子模块 S 会做什么,那么简而言之:

  1. 确保已导入 P,否则导入它。 (此步骤递归处理“import A.B.C.D”。)
  2. 执行 S.py 获取模块对象。 (跳过 .pyc 文件等的详细信息)
  3. 在 sys.modules["P.S"] 中存储模块对象。
  4. setattr(sys.modules["P"], "S", sys.modules["P.S"])
  5. 如果该导入的形式为“import P.S”,则在本地范围内绑定“P”。

【讨论】:

  • 而且解析包的__init__.py里面的非限定名的范围包括包对象的属性吧?
  • @yole:是的,__init__.py 中的 globals() 是 sys.modules["package"].__dict__。
【解决方案2】:

这是因为__init__.py 在运行时将自己表示为 package1 模块对象,因此每个 .py 文件都将被定义为一个子模块。并重写__all__ 将没有任何意义。您可以创建另一个文件,例如 example.py 并在__init__.py 中填充相同的代码,它将引发NameError

我认为当__init__.py寻找变量与其他python文件不同时,CPython运行时采用特殊算法,可能是这样的:

looking for variable named "moduleB"
if not found:
   if __file__ == '__init__.py': #dont raise NameError, looking for file named moduleB.py
         if current dir contains file named "moduleB.py":
                      import moduleB
         else:
                    raise namerror

【讨论】:

  • 这不是那么简单。如果在 __init__.py 中不导入 moduleB,则名称不会被解析。
  • @yole 是的,对不起。修改后忘记重启python shell
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-12-01
  • 2021-11-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-06-16
相关资源
最近更新 更多