为什么 Python 2 在使用 "import ... as ..." 时尝试获取模块作为包属性?

Why does Python 2 try to get a module as a package attribute when using "import ... as ..."?

EDIT: closely related to Imports in __init__.py and import as statement. That question deals with the behaviour of import ... as ... for up to Pyhon 3.6. The change in behaviour I'm describing below was introduced in Python 3.7, with the intention to fix the bug described in that other question. I'm more interested in where the change is documented (or where the two different behaviours, for Py2 up to Py3.6 vs Py3.7+, are respectively documented) rather than how exactly this behaviour arises (as I already mostly understand that as a result of experimenting in preparation for this question).


考虑以下目录结构:

.
└── package
    ├── __init__.py
    ├── a.py
    └── b.py

__init__.py 文件为空。 package.apackage.b 两个模块分别包含:

# package.a
import sys

print('package.b' in sys.modules)
import package.b as b

spam = 'ham'
print("{} says b is {}".format(__name__, b))
# package.b
import package.a

print("{} says package.a.spam is {}".format(__name__, repr(package.a.spam)))


Python3.x(具体是3.8),当我从根目录运行python -c "from __future__ import print_function; import package.b"时,我得到

True
package.a says b is <module 'package.b' from 'C:\[...]\package\b.py'>
package.b says package.a.spam is 'ham'

但是使用 Python 2.x(特别是 2.7)我得到

True
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "package\b.py", line 1, in <module>
    import package.a
  File "package\a.py", line 4, in <module>
    import package.b as b
AttributeError: 'module' object has no attribute 'b'

问题是:是什么保证了这种差异?此更改记录在何处,例如Python 文档、PEP 或类似文件?



我知道当 package.a 被导入时 package.b 还没有完成初始化,所以 package.b 的模块对象还没有被添加为模块对象的属性对于 package。但是模块对象本身存在(因为它被添加到 sys.modules),所以将名称 b 绑定到该对象应该没有任何问题,这就是 Python 3 所做的我相信? Python 2 似乎没有将它直接绑定到模块对象,而是试图通过从 package.

的模块对象中获取名为 'b' 的属性来获取它

据我所知,文档中没有这样的规范。

Import statement (Python 3.8):

If the requested module is retrieved successfully, it will be made available in the local namespace in one of three ways:

  • If the module name is followed by as, then the name following as is bound directly to the imported module.

[...]

Import statement (Python 2.7):

The first form of import statement binds the module name in the local namespace to the module object, and then goes on to import the next identifier, if any. If the module name is followed by as, the name following as is used as the local name for the module.



备注:

如果你这样做

import package.submodule

然后尝试访问 package.submodule,该访问是对 package 的模块对象的属性查找,它将找到绑定到 submodule 属性的任何对象(如果未设置该属性,则失败)。为了保持一致性,

import package.submodule as whatever

执行相同的属性查找以找到要绑定到的对象 whatever

Python 3.5 中的 changed in Python 3.7 to fall back to a sys.modules['package.submodule'] lookup if the attribute lookup fails. This change was made for consistency with a previous change 使 from package import submodule 回退到 sys.modules 查找, 更改是为了让相对导入更方便。