fil*_*den 5 python circular-dependency python-import python-2.7 python-3.x
注意:这是关于导入模块而不是那些模块中的类,函数,所以我认为它不是鬃毛"ImportError:无法导入名称"的重复结果,至少我没有找到一个匹配这个.
我确实理解,按名称从模块导入类或函数可能会导致问题,因为如果存在循环依赖关系,模块本身可能尚未完全初始化,但这不是这里的情况.
要重现此问题,请创建三个对其具有循环依赖关系的模块.
首先创建一个包:
$ mkdir pkg
$ touch pkg/__init__.py
Run Code Online (Sandbox Code Playgroud)
然后创建pkg/a.py,内容为:
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))
Run Code Online (Sandbox Code Playgroud)
和pkg/b.py,内容如下:
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)
Run Code Online (Sandbox Code Playgroud)
和pkg/c.py,内容如下:
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)
Run Code Online (Sandbox Code Playgroud)
以及调用它们的main.py(在顶层目录中):
from __future__ import print_function
from __future__ import absolute_import
from pkg import a
if __name__ == '__main__':
a.A(5)
Run Code Online (Sandbox Code Playgroud)
我预计循环依赖会没有问题,因为在导入时间内没有对每个模块中的项的引用(即模块b或c中没有引用aA,除了cC体内的调用).
事实上,使用python3运行它可以正常工作:
$ 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!
Run Code Online (Sandbox Code Playgroud)
(这是Debian Stretch上的Python 3.5.3,用于记录.)
但是使用python2(Python 2.7.13),它并没有真正起作用,它抱怨循环依赖...
$ 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
Run Code Online (Sandbox Code Playgroud)
所以我的问题是:
为什么我遇到循环依赖问题,如果我不是从模块中导入或引用特定的类或函数,只是模块本身?
为什么这只发生在Python 2上?(参考PEP,代码,发行说明或有关Python 3中的修复程序的文章将不胜感激.)
有没有办法在Python 2中避免这个问题,同时仍然没有打破模块的循环依赖?我相信并非所有的循环依赖都会导致这个问题(即使在Python 2中),所以我想知道哪些情况是安全的,哪些情况不是......
当Python开始加载pkg.a
模块时,它会设置sys.modules['pkg.a']
相应的模块对象,但它只在加载模块的最后才设置模块对象a
的属性。这将在稍后相关。pkg
pkg.a
相对导入就是from
导入,它们的行为是相同的。from . import whatever
在弄清楚 指.
的是包之后pkg
,它继续执行常规from pkg import whatever
逻辑。
当c.py
点击时from . import a
,首先看到 已经pkg.a
在 中sys.modules
,表示pkg.a
已经加载或者正在加载中。(它正在加载中,但此代码路径并不关心。)它跳到其工作的第二部分,检索pkg.a
并将其分配给a
本地命名空间中的名称,但它不仅仅检索sys.modules['pkg.a']
要做的事情这。
你知道如何做类似的事情from os import open
,即使它os.open
是一个函数,而不是一个模块?这种导入无法通过sys.modules['os.open']
,因为它os.open
不是模块,也不在sys.modules
. 相反,所有from
导入(包括所有相对导入)都会尝试在从中导入名称的模块上进行属性查找。from . import a
查找模块对象a
上的属性,但它不存在,因为该属性仅在完成加载pkg
时设置。pkg.a
在 Python 2 上,就是这样。导入结束。ImportError
这里。在Python 3(特别是3.5+)上,因为他们想鼓励相对导入,而这种行为确实很不方便,所以from
导入尝试多了一步。如果属性查找失败,现在他们会尝试sys.modules
。pkg.a
位于 中sys.modules
,因此导入成功。您可以在 CPython 问题跟踪器的问题 17636中查看有关此更改的讨论。
归档时间: |
|
查看次数: |
424 次 |
最近记录: |