为什么 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.a
和 package.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.
备注:
- 在
package/a.py
中使用 from package import b
产生相同的结果,只是错误不同(即 ImportError
而不是 AttributeError
)。我怀疑 ImportError
只是包裹了底层的 AttributeError
.
- 在
package/a.py
中使用 import package.b
在 Py2 中导入时不会给出 AttributeError
。但是,当然,稍后在 print 调用中引用 package.b
在 Py2 和 Py3 中都会产生一个 AttributeError
。
如果你这样做
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
查找, 更改是为了让相对导入更方便。
EDIT: closely related to Imports in __init__.py and
import as
statement. That question deals with the behaviour ofimport ... 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.a
和 package.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.
备注:
- 在
package/a.py
中使用from package import b
产生相同的结果,只是错误不同(即ImportError
而不是AttributeError
)。我怀疑ImportError
只是包裹了底层的AttributeError
. - 在
package/a.py
中使用import package.b
在 Py2 中导入时不会给出AttributeError
。但是,当然,稍后在 print 调用中引用package.b
在 Py2 和 Py3 中都会产生一个AttributeError
。
如果你这样做
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
查找, 更改是为了让相对导入更方便。