Reputation: 1054
I have the following file structure, each with at most one line of code (shown below):
a
├── b
│ ├── c.py import a.b.d as d
│ ├── d.py
│ └── __init__.py from a.b.c import *
├── __init__.py
└── main.py import a.b as b
By running python -m a.main
, I get the following error:
Traceback (most recent call last):
File "/usr/lib/python2.7/runpy.py", line 162, in _run_module_as_main
"__main__", fname, loader, pkg_name)
File "/usr/lib/python2.7/runpy.py", line 72, in _run_code
exec code in run_globals
File "/tmp/test/a/main.py", line 1, in <module>
import a.b as b
File "a/b/__init__.py", line 1, in <module>
from a.b.c import *
File "a/b/c.py", line 1, in <module>
import a.b.d as d
AttributeError: 'module' object has no attribute 'b'
I am not sure if this is caused by circular import. If I change import a.b.d as d
to from a.b import d
, there is no error any more.
Upvotes: 2
Views: 404
Reputation: 306
The AttributeError
is caused by the as
in the import statement in file c.py
.
The whole process is like:
main.py
created module a
, added it to sys.modules
and initialized it;main.py
created module a.b
, added it to sys.modules
and began to execute its code;b/__init__.py
(a
and a.b
are already in sys.modules
) created module a.b.c
, added it to sys.modules
and begin to execute its code;b/c.py
created module a.b.d
, added it to sys.modules
, executed its code, added it as an attribute 'd' of module a.b
, then tried but failed to bind a.b.d
to name d
. The problem is that module a.b
was not done initializing yet, so the attribute 'b' was not in module a
.WHY
To understand this, you should know that an import statement does two things (in both Python 2 and Python 3).
Module Finding
The former one calls the __import__
hook, which loads the module and initializes it. In Python 2 the hook is imputil.ImportManager._import_hook
by default, and it works like this.
sys.modules
;sys.modules
;If the statement is like import a.b.c
, the module finding process will recursively find module a
, a.b
, a.b.c
, and keep track of them in sys.modules
. If module a.b
is returned, it is set as an attribute 'b' of module a
. Finally, the module finding process will return the top module a
.
If the statement is like from a.b import c,d
, the outcome is a little different. In this case the bottom module (i.e. module a.b
) will be returned.
Name Binding
If you use an import [module]
statement, the name of the top module will be bound to the return value (which is the top module).
If you use an import [module] as [name]
statement, then [name]
will be bound to the bottom module (by accessing the attributes of the top module).
If you use an from [module] import [identifier]
, then the name of the bottom module will be bind to the return value (which in from import
statement is the bottom module).
Example
import a.b.c # a <- <module 'a'>
import a.b.c as c # c <- <module 'a'>.b.c
from a.b import c # c <- <module 'a.b'>.c
In your question, the import statement in c.py
occurs when module a.b
is half initialized and is not yet registered in module a
's attributes. So import as
will encounter a problem when binding a.b.c
to c
. However, since module a.b
is already registered in sys.modules
, using from import
will not encounter such a problem.
Upvotes: 3