Suppose I have the following code.
foo.py
------
import bar
def abc(n):
return bar.xyz(n-1) if n>0 else "abc"
bar.py
------
import foo
def xyz(n):
return foo.abc(n-1) if n>0 else "xyz"
As explained in this post Circular (or cyclic) imports in Python, it will work. (The short explanation is, suppose we call import foo from python repl, when import bar is first encountered, python puts bar into sys.modules and executes bar.py, and when import foo is encountered, since foo is already in sys.modules, the import statement directly returns even though foo module is incomplete.)
Now if I change the code into the following:
foo.py
------
from bar import xyz
def abc(n):
return xyz(n-1) if n>0 else "abc"
bar.py
------
from foo import abc
def xyz(n):
return abc(n-1) if n>0 else "xyz"
The import will fail:
$ python foo.py
Traceback (most recent call last):
File "foo.py", line 1, in <module>
from bar import xyz
File "/Users/yxiong/bar.py", line 1, in <module>
from foo import abc
File "/Users/yxiong/foo.py", line 1, in <module>
from bar import xyz
ImportError: cannot import name xyz
What is worth noting here is python seems to fail on the second try of from bar import xyz.
My question is, what is exactly going on at those steps. Specifically, what does python do when it sees a from foo import abc statement?
Walk through the frames, first Python attempts to load/compile the
foomodule (to be known as__main__, only compiles it once, but will be executed twice):Python attempts to execute the import statement. So since Python only loads/compiles modules once (unless using
reload), it looks insys.modulesand since it isn't there, it attempts to load/compile thebarmodule to importxyz.But
barattempts to load/compilefooand importabc, which we see is already insys.modules. And we're back to our original import:and we get the ImportError:
Let's empirically demonstrate, from a terminal in Unix:
ctrl-d
ctrl-d
Next experiment:
ctrl-d