我第一次遇到这个问题是在一个更大的现有图书馆的背景下,这些图书馆急需重组。然而,现在不是时候,一个问题已经出现后,最近一系列的变化。我创建了一个抽象的例子,它与真实的库有着完全相同的问题。在
我们有以下文件结构:
|- pack
| |- sub_a
| | |- __init__.py
| | |- a_func.py
| |- sub_b
| | |- __init__.py
| | |- b_func.py
| |- __init__.py
|- runit.py
主程序是runit.py
,它从pack
包中导入所需的特定内容并运行它。在
runit.py
:
包包由其余文件组成,以pack\__init__.py
开头:
pack\sub_a\__init__.py
:
from .a_func import fa, fab
pack\sub_a\a_func.py
:
from ..sub_b import fb
def fa():
return 0
def fab():
return fb()
pack\sub_b\__init__.py
:
from .b_func import fb, fba
pack\sub_b\b_func.py
:
from ..sub_a import fa
def fb():
return 1
def fba():
return fa()
现在的问题是:如果你试图逃跑运行它.py,失败的原因是:
Traceback (most recent call last):
File "C:/adir/runit.py", line 1, in <module>
import pack
File "C:\adir\pack\__init__.py", line 1, in <module>
from .sub_a import fa, fab
File "C:\adir\pack\sub_a\a_func.py", line 1, in <module>
from ..sub_b import fb
File "C:\adir\pack\sub_b\__init__.py", line 1, in <module>
from .b_func import fb, fba
File "C:\adir\pack\sub_b\b_func.py", line 1, in <module>
from ..sub_a import fa
ImportError: cannot import name 'fb' 'fa' from 'pack.sub_a' (C:\adir\pack\sub_a\__init__.py)
但是,如果您删除fa
导入(并在fba()
中更改使用它的行),它仍然可以工作,尽管a_func.py
中的fb
的导入相同。在
原因是sub_a
需要来自sub_b
的东西,而{
当然,在这个例子中,循环导入是愚蠢的。最简单的解决方案是将所有函数放在一个位置;然后,由于函数之间没有循环依赖关系,因此没有问题。在
但在实际情况下,有些循环引用是有充分理由的(我说“有点”是因为函数没有实际的循环依赖关系,但当然Python直到运行时才知道这一点)。在
我看到的唯一选择是:
fa()
和/或{fba()
定义的内部fba()
可以有一个callback
参数我不喜欢前四个:选项1。成为一个痛苦的阅读维护。选项2。这意味着,当前编写得相当好并且在一起很有意义的代码将需要分成两个或多个部分。选项3。看起来很像反模式,因为这使得很难看到任何模块有什么依赖关系。在选项4中,现有函数的调用约定会全面改变。在
我最初担心的是选项3。模块主体中的任何代码都将在每次导入时执行;但是@a_guest指出“模块被缓存(在sys.modules
)中,并且在需要时将从缓存中获取,而不是再次执行模块的代码”
我倾向于使用选项5。在这种情况下,重写pack\sub_b\b_func.py
,如下所示:
def initialise():
global fa
from pack import fa as pack_fa
fa = pack_fa
def fb():
return 0
def fba():
return fa()
更新pack\sub_b\__init__.py
:
from .b_func import fb, fba, initialise
然后pack\__init__.py
变成:
from .sub_a import fa, fab
from .sub_b import fb, fba
for module in [sub_a, sub_b]:
if hasattr(module, 'initialise'):
module.initialise()
通过在实际调用关闭循环的函数之前完成所有导入,这就解决了这个问题,因为函数内部没有实际的循环,所以不会引起冲突。在
这里的缺点是pack的__init__.py
现在意识到了初始化sub_b
模块的必要性,并且该模块本身有一些额外的代码,这些代码只是作为解决这个问题的一种解决办法,但是所有其他代码都与预期的一样工作(新的和遗留的)。在
只要任何导入sub_b
的人在使用其内容之前总是至少调用initialise
一次,它甚至可以在pack
之外使用,前提是有正确的依赖关系。在
我的第六个问题是,有更好的选择吗?
注意:请注意,我不是在寻找什么是更好的意见,尽管如果你有一个强有力的、理性的论据来解释为什么这些选择中的一个必须被认为是更优越的,它可能会回答我的问题,但我真的在寻找一个更好的选择,我只是在这里错过了。我不是在为这种类型的代码设计辩护——它本应该被避免的,但事实并非如此。
免责声明:我知道关于Python中循环依赖和循环导入的其他问题,但我不认为我的问题会重复这些问题,因为没有一个深入到森林中并提出最后一个问题
目前没有回答
相关问题 更多 >
编程相关推荐