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).
Consider the following directory structure:
.
└── package
├── __init__.py
├── a.py
└── b.py
The __init__.py
file is empty. The two modules package.a
and package.b
contain, respectively:
# 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)))
With Python 3.x (specifically 3.8), When I run python -c "from __future__ import print_function; import package.b"
from the root directory, I get
True
package.a says b is <module 'package.b' from 'C:\\[...]\\package\\b.py'>
package.b says package.a.spam is 'ham'
but with Python 2.x (specifically 2.7) I get
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'
The question is: what warrants this difference? Where is this change documented, e.g. the Python docs, a PEP or similar?
I get that package.b
hasn't finished initialising when package.a
is imported, so the module object for package.b
hasn't yet been added as an attribute of the module object for package
. But yet the module object itself exists (as it is added to sys.modules
), so there shouldn't be any trouble binding the name b
to that object, which is what Python 3 does I believe? Python 2 seems like it's not binding it directly to the module object, but rather trying to fetch it by getting an attribute named 'b'
from the module object for package
.
As far as I can see, there is no such specification in the documentation.
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.
Notes:
- Using
from package import b
inpackage/a.py
yields the same, only with a different error (i.e.ImportError
instead ofAttributeError
). I suspect theImportError
is just wrapping the underlyingAttributeError
. - Using
import package.b
inpackage/a.py
doesn't give theAttributeError
upon import in Py2. But, of course, referencingpackage.b
later in the print call produces anAttributeError
in both Py2 and Py3.