Imports in __init__.py and 'import as' statement
You incorrectly assume that one cannot have an alias with from ... import
, as from ... import ... as
has been there since Python 2.0. The import ... as
is the obscure syntax that not many know about, but which you use by accident in your code.
PEP 0221 claims that the following 2 are "effectively" the same:
import foo.bar.bazaar as baz
from foo.bar import bazaar as baz
The statement is not quite true in Python versions up to and including 3.6.x as evidenced by the corner case you met, namely if the required modules already exist in sys.modules
but are yet uninitialized. The import ... as
requires that the module foo.bar
is injected in foo
namespace as the attribute bar
, in addition to being in sys.modules
, whereas the from ... import ... as
looks for foo.bar
in sys.modules
.
(Do note also that import foo.bar
only ensures that the module foo.bar
is in sys.modules
and accessible as foo.bar
, but might not be fully initialized yet.)
Changing the code as follows did the trick for me:
# import pkg.subpkg.two_longname as two
from pkg.subpkg import two_longname as two
And code runs perfectly on both Python 2 and Python 3.
Also, in one.py
you cannot do from pkg import subpkg
, for the same reason.
To demonstrate this bug further, fix your one.py
as above, and add the following code in tst.py
:
import pkg
import pkg.subpkg.two_longname as two
del pkg.subpkg
from pkg.subpkg import two_longname as two
import pkg.subpkg.two_longname as two
Only the last line crashes, because from ... import
consults the sys.modules
for pkg.subpkg
and finds it there, whereas import ... as
consults sys.modules
for pkg
and tries to find subpkg
as an attribute in the pkg
module. As we just had deleted that attribute, the last line fails with AttributeError: 'module' object has no attribute 'subpkg'
.
As the import foo.bar as baz
syntax is a bit obscure and adds more corner cases, and I have rarely if ever seen it being used, I would recommend avoiding it completely and favouring from .. import ... as
.
Here is a theory on what's going on.
When you use the as
reserved word, for instance:
import pkg.subpkg.two_longname as two
Python must to completely initialize and resolve all dependences that has to do with pkg.subpkg
. But there is a problem, to completely load subpkg
you need to completely load one.py
as well right? wich at the same time imports two_longname.py
using the as
keyword ... Can you see the recursion here? That's why at the moment of doing:
import pkg.subpkg.two_longname as two
you get an error claiming subpkg
does not exist.
To perform a test, go to one.py and change it to this:
#import pkg.subpkg.two_longname as two
from pkg.subpkg import two_longname
#class One(two.Two):
class One(two_longname.Two):
pass
I suppose this is all about performance, Python loads a module partially whenever is possible. And the as
keyword is one of the exceptions. I don't know if there are others, but it would be interesting know about them.