Python:来自模块的循环导入成员

时间:2014-11-09 20:32:24

标签: python python-import circular-dependency

假设我有以下代码。

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"

正如本文Circular (or cyclic) imports in Python中所述,它会起作用。 (简短的解释是,假设我们从python repl调用import foo,首次遇到import bar时,python将bar放入sys.modules并执行bar.py,并且遇到import foo时,由于foo已经在sys.modules,因此即使foo模块不完整,导入语句也会直接返回。)

现在,如果我将代码更改为以下内容:

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"

导入将失败:

$ 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

值得注意的是,在from bar import xyz的第二次尝试中,python似乎失败了。

我的问题是,这些步骤究竟发生了什么。具体来说,当python看到from foo import abc语句时会做什么?

1 个答案:

答案 0 :(得分:0)

遍历框架,首先Python尝试加载/编译foo模块(称为__main__,仅编译一次,但将执行两次):

Traceback (most recent call last):
  File "foo.py", line 1, in <module> 
    from bar import xyz # first attempt to import xyx, but it depends on abc

Python尝试执行import语句。 因此,由于Python只加载/编译模块一次(除非使用reload),它会查找sys.modules,因为它不在那里,它会尝试加载/编译bar模块导入xyz

  File "/Users/yxiong/bar.py", line 1, in <module>
    from foo import abc # so we attempt to import abc, but it depends on xyz

但是bar尝试加载/编译foo并导入abc,我们看到它已经在sys.modules中。我们回到原来的导入:

  File "/Users/yxiong/foo.py", line 1, in <module>
    from bar import xyz 

我们得到了ImportError:

ImportError: cannot import name xyz

让我们从Unix的终端凭经验证明:

cat > foo.py
print('executing ' + __name__)
from bar import xyz
def abc(n):
    return xyz(n-1) if n>0 else "abc"

CTRL-d

cat > bar.py
print('executing ' + __name__)
from foo import abc
def xyz(n):
    return abc(n-1) if n>0 else "xyz"

CTRL-d

python foo.py

下一个实验:

cat > main.py
print('executing ' + __name__)
import foo

CTRL-d

python main.py