如何处理Python模块中的循环依赖?

8

这是我再次陷入循环的一个案例,我快要疯了。

我希望Python能够首先分析所有文件,以便从一开始就知道所有标识符(就像Java那样)。

我有一个“main.py”和一个“gui.py”。每个文件都包含一个类,该类使用另一个文件中的类。当我尝试运行“main.py”时,解释器会导入“gui”,然后在“gui.py”中导入“main”,然后处理整个主模块并说:“嘿嘿,gui.py中没有给定名称的类。”

如何在Python中处理循环依赖关系,最小化麻烦

3个回答

17

我觉得把这个扩展成答案而不是评论会更好。

值得注意的是,循环引用通常是糟糕设计的标志:与其要求语言适合您的设计,为什么不改变设计呢?

在Python中有解决此问题的方法

  • 好的选择:重构代码以不使用循环引用。
  • 坏的选择:将您的一个import语句移动到不同的作用域中。

但是,不,您无法预解析文件。 这不是Python的工作方式,如果您深入了解Python的工作原理,显然就可以知道为什么。


谢谢大家。现在对我来说有点清晰了。就像观察者模式:一个类是另一个类的守护者(结构上,内容上相反)。具有较少概述的类只需告诉已注册的侦听器:“嗯,对不起,我已经改变了。”我忽略了在“main.py”中必须区分应用程序逻辑类的代码和首先使程序运行的代码。我已将一个导入放在类下面并放在独立代码上面。 - rynd
将导入语句移到文件末尾并不能解决问题。如果它在模块范围内的任何位置被创建,就会触发循环导入。将导入语句移到不同的范围将会解决这个问题(请参见下面的答案)。 - jcdyer
@jcdyer 已经更正,对于在那种情况下提供更多细节的回答给予+1。 - Gareth Latty
4
在ORM模型中,如果ObjectA有多个ObjectB,并且ObjectB属于ObjectA,这是一种常见的循环依赖关系,看起来非常合理。有没有好的方法从设计中消除这种依赖关系? - Zac Witte
@ZacWitte 选项2 - 将循环导入移动到仅在第一次导入完成后执行的位置。 - Gareth Latty
谢谢。我使用的是 from . import stuff 表示法,它完全拒绝允许循环导入。但将导入放在使用它的方法内可以解决这个问题。 - Jack O'Connor

7

如果无法避免循环导入,请将其中一个导入移出模块级别的作用域,并移到使用它的方法/函数中。

filea.py

import fileb

def filea_thing():
    return "Hello"

def other_thing():
    return fileb_thing()[:10]

fileb.py

def fileb_thing():
    import filea
    return filea.filea_thing() + " everyone."

那么,只有在调用fileb_thing()时才会导入filea,然后重新导入fileb,但由于此时不会调用fileb_thing,因此不会保持循环。

正如其他人指出的那样,这是一种代码气味,但有时即使很丑也需要完成某些事情。


3
一般来说,依赖关系应该是一棵树。循环依赖关系是无法解决的。
通常解决此问题的方法是,在全局命名空间之外的某个级别进行所需模块的“本地导入”。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接