Understanding behavior of Python imports and circular dependencies
注意:这是关于导入模块而不是那些模块中的类,函数,所以我认为这不是鬃毛"ImportError:无法导入名称"结果的重复,至少我没有找到一个匹配这个。
我确实理解,按名称从模块导入类或函数可能会导致问题,因为如果存在循环依赖关系,模块本身可能尚未完全初始化,但这不是这种情况。
要重现此问题,请创建三个对其具有循环依赖关系的模块。
首先创建一个包:
1 2 | $ mkdir pkg $ touch pkg/__init__.py |
然后创建pkg / a.py,内容为:
1 2 3 4 5 6 7 8 9 10 11 | from __future__ import print_function from __future__ import absolute_import from . import b def A(x): print('I am A, x={}.'.format(x)) b.B(x + 1) def Z(x): print('I am Z, x={}. I\'m done now!'.format(x)) |
和pkg / b.py,内容如下:
1 2 3 4 5 6 7 8 | from __future__ import print_function from __future__ import absolute_import from . import c def B(x): print('I am B, x={}.'.format(x)) c.C(x * 2) |
和pkg / c.py,内容如下:
1 2 3 4 5 6 7 8 | from __future__ import print_function from __future__ import absolute_import from . import a def C(x): print('I am C, x={}.'.format(x)) a.Z(x ** 2) |
以及调用它们的main.py(在顶层目录中):
1 2 3 4 5 6 7 | from __future__ import print_function from __future__ import absolute_import from pkg import a if __name__ == '__main__': a.A(5) |
我期望循环依赖性没有问题,因为在导入期间没有对每个模块中的项的引用(即,除了c.C体内的调用之外,没有从模块b或c引用a.A)。
事实上,使用python3运行它可以正常工作:
1 2 3 4 5 | $ python3 main.py I am A, x=5. I am B, x=6. I am C, x=12. I am Z, x=144. I'm done now! |
(这是Debian Stretch上的Python 3.5.3,用于记录。)
但是使用python2(Python 2.7.13),它并没有真正起作用,它抱怨循环依赖...
1 2 3 4 5 6 7 8 9 10 11 | $ python main.py Traceback (most recent call last): File"main.py", line 5, in <module> from pkg import a File"/tmp/circular/pkg/a.py", line 5, in <module> from . import b File"/tmp/circular/pkg/b.py", line 5, in <module> from . import c File"/tmp/circular/pkg/c.py", line 5, in <module> from . import a ImportError: cannot import name a |
所以我的问题是:
-
为什么我遇到循环依赖问题,如果我不是从模块中导入或引用特定的类或函数,只是模块本身?
-
为什么这只发生在Python 2上? (参考PEP,代码,发行说明或有关Python 3中的修复程序的文章将不胜感激。)
-
有没有办法在Python 2中避免这个问题,同时仍然没有打破模块的循环依赖?我相信并非所有的循环依赖都会导致这个问题(即使在Python 2中),所以我想知道哪些情况是安全的,哪些情况不是......
当Python开始加载
相对导入是
当
你知道怎么做
在Python 2上,就是这样。进口结束。
我不确定Python 3是如何解决这个问题的,但是我的经验告诉Python 2真的无法使它工作。解决问题的正确方法是:
我个人更喜欢后者。
为什么,Python中的模块系统不会标记成功加载的模块。所以在你的"import a"中,Python不会知道它已经加载了"a",直到所有相关的加载,"b"和"c"完成,因为它通过了整个"a.py"文件。因此,在处理"导入c"时,它将再次尝试"导入"而不是发现"a"是它可以跳过的东西。