【问题标题】:Python: Circular import members from modulePython:来自模块的循环导入成员
【发布时间】:2015-01-06 02:51:17
【问题描述】:

假设我有以下代码。

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时,因为@987654329 @已经在sys.modules中,即使foo模块不完整,import语句也会直接返回。)

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

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

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

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

【问题讨论】:

  • 加一个有趣的问题。

标签: python python-import circular-dependency


【解决方案1】:

遍历框架,首先 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

【讨论】:

  • 谢谢@Aaron。我做了另一个实验:从 python repl 运行 import foo,这次它在 cannot import name abc 失败(比从命令行运行 python foo.py 少一帧)。正如您所解释的,在命令行中运行时,foo.py 首先作为__main__ 模块运行,这就是为什么python 在遇到bar.py 时再次尝试import foo - foo 不是 sys.modules 中。另一方面,如果从 repl 运行,foo 位于 sys.modules 中,因此 python 声称失败而不会再次尝试 import
  • 编辑澄清,因为我打算通知它只导入一次。
  • 其实我不同意。如果我将print __file__, __name__ 添加到两个文件中,并从命令行运行python foo.py,我得到3 个打印:foo.py __main__bar.py barfoo.py foo;如果从 repl 运行,则只有 2 次打印 foo.py foobar.py bar。这意味着在命令行中运行时,foo 确实被导入了两次,第一次为__main__,第二次为foo
  • here所述,“执行脚本在名为__main__的模块中运行,以自己的名称导入脚本将创建一个与__main__无关的新模块。”
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-05-15
  • 2012-08-30
  • 1970-01-01
  • 2021-08-31
  • 2011-09-15
  • 1970-01-01
相关资源
最近更新 更多