Python cyclic imports fail when using from package import module syntax [duplicate]
-
30-06-2021 - |
Question
Possible Duplicate:
Cyclic module dependencies and relative imports in Python
Consider the following example of cyclic imports in python:
main.py:
from pkg import foo
pkg/__init.py__:
# empty
pkg/foo.py:
from pkg import bar
pkg/bar.py:
from pkg import foo
Running main.py will cause an exception:
Traceback (most recent call last):
File "/path/to/main.py", line 1, in <module>
from pkg import foo
File "/path/to/pkg/foo.py", line 1, in <module>
from pkg import bar
File "/path/to/pkg/bar.py", line 1, in <module>
from pkg import foo
ImportError: cannot import name foo
Changing bar.py to:
# from pkg import foo
import pkg.foo
will make the example work.
Why does this happen? Shouldn't *import package.module" and "from package import module" be equivalent (except for the extra name binding in the latter)?
Solution
First of all you have to understand that:
The
sys.modules
variable play the role of a cache in the import mechanize which mean that if we import a module the first time, an entry containing the name of this module is added tosys.modules
, so that when we try to import this same module next time, we will only get the already cached module fromsys.modules
and we will not execute the module again.The difference between
import pkg.foo
andfrom pkg import foo
is that the second is equivalent too:import pkg.foo
followed bygetattr(pkg, 'foo')
. (check my answer here for more detail)
Now what happen in your first example is the following:
1- In main.py
: we start by executing the line from pkg import foo
, so first the entry pkg.foo
is added to sys.modules
(i.e. 'pkg.foo' in sys.modules == True
) than we try to import foo
.
2- In foo.py
: While importing foo
we end up executing this line: from pkg import bar
and again the entry pkg.bar
is added to sys.modules
; and than we start importing bar
, what is important to note here is that we are still executing foo.py
, so we are kind off still executing this line from pkg import foo
from main.py
.
3- In bar.py
: while now importing bar.py
we end up executing the line: from pkg import foo
, but remember we have already an entry in sys.modules
for pkg.foo
which mean that executing foo.py
will be skipped and the import mechanize will get us instead the sys.modules['pkg.foo']
entry, and now we will excute getattr(pkg, 'foo')
as explained above, but remember 'pkg.foo' didn't finish importing; which mean that there is no attribute foo
which explain the error.
Now if you put in bar.py
only import pkg.foo
this will not have the line that cause us the problem last time which is: getattr(pkg, 'foo')
.
HTH,