【问题标题】:Resolving circular dependencies in a python/django application解决 python/django 应用程序中的循环依赖关系
【发布时间】:2019-06-04 22:28:06
【问题描述】:

我有一个调用文件解析器(解析文件)的模型,并且该文件解析器调用模型来保存对象。目前,代码如下所示:

models.py

class Source(models.Model):
    ...

    def parse_file(self):
        from ingest.parser import FileParser
        ...

摄取.py

class FileParser()

    def save(self):
        from models import Source
        ...

这个“工作”很好,但是,在 save 方法中进行导入会在我第一次使用它时添加大约 0.25s,因为它正在初始化导入。有没有更好的方法来完成上述操作?

【问题讨论】:

  • 为什么不在文件顶部导入,而不是在方法内部
  • @VaibhavVishal 否则会给我一个循环导入错误。
  • 您是否尝试过使用get_model 函数来通过字符串名称获取模型?
  • 出于好奇,您最终更喜欢哪种解决方案?
  • @MadPhysicist 我使用了您在下面建议的“将其放在底部”的方法。但是,是否有标准做法可以做到这一点?例如,您是否应该在顶部包含一个注释导入,上面写着“文件底部的实际导入以解决循环依赖关系”,或者您的文件通常看起来像这样实现它?

标签: python django python-3.x


【解决方案1】:

第一次加载模块时,具有空命名空间的模块对象会立即放入sys.modules。执行模块代码时会填充命名空间。对模块的任何进一步引用只会检索 sys.modues 中的引用,无论它是否已完全加载。这产生了两种解决问题的方法。

方法一

由于导入的名称不会在方法之外使用,因此您只需确保它们在调用方法时就存在,而不是在首次创建时。

您可以通过将有问题的导入放在各自文件的末尾来解决导入问题。这样,无论哪个模块首先加载,它中的所有顶级名称都将在其他模块尝试访问它们之前被初始化:

models.py

class Source(models.Model):
    ...

    def parse_file(self):
        ...

from ingest.parser import FileParser

摄取.py

class FileParser()
    def save(self):
        ...

from models import Source

如果首先加载models.py,则from ingest.parser import FileParser 行将触发ingest.py 的加载,但仅在模块命名空间中定义Source 之后。这意味着from models import Source 将能够找到该名称。以相反的顺序同样适用。

如果您知道总是首先加载哪个模块,那么只需将其中一个导入移动到文件末尾(文件中首先加载的那个)。

方法二

一个更简单的选择可能是只导入模块,而不是尝试从中提取名称。这将允许您将导入保留在文件的顶部,因为可以使用一个空模块对象来满足循环导入:

models.py

from ingest import parser

class Source(models.Model):
    ...

    def parse_file(self):
        # use parser. FileParser
        ...

摄取.py

import models

class FileParser()
    def save(self):
        # use models.Source
        ...

【讨论】:

  • 很好的答案。非常感谢您的周到。
  • 不客气。我已经多次遇到这个问题,我可以发誓你的问题是重复的,但我似乎找不到足够精确的匹配项。
猜你喜欢
  • 1970-01-01
  • 2018-10-13
  • 2010-10-28
  • 2014-09-06
  • 2021-04-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多