我知道有很多问题都在谈论循环导入。我看过很多,但我似乎无法弄清楚如何将它们应用到这个场景中。
我有一对数据加载类,用于从具有多个工作表的 Excel 文档导入数据。每个工作表都是可配置的,因此工作表名称和各个列名称可以更改(但它们在类属性中定义了默认值)。
所讨论的两个类之间还有其他类间引用,但我认为这个例子是最直接的:
单独导出脚本的功能之一是使用加载程序的元数据填充 Excel 模板(具有多个工作表)。该模板在每个工作表中引用其他工作表的列标题上都有注释,因为某些工作表的内容用于填充其他工作表中的下拉列表。
因此,一张工作表标题中的注释可能会显示“此列的下拉数据由工作表 2 中 X 列的内容填充”。并且工作表 2 的 X 列标题将有一条注释,内容为“此列的内容用于填充工作表 1 中 Y 列的下拉列表。”
我继续添加各自的导入,知道我最终会遇到循环导入问题,但我想我会在概念上建立关于我想要做的一切,然后尝试解决导入问题。
这里有一些玩具代码,可以尝试将其归结为:
infusates_loader.py
:
from DataRepo.loaders.tracers_loader import TracersLoader
class InfusatesLoader(TableLoader):
DataColumnMetadata = DataTableHeaders(
TRACERNAME=TableColumn.init_flat(
...
source_sheet=TracersLoader.DataSheetName,
source_column=TracersLoader.DataHeaders.NAME,
),
)
tracers_loader.py
:
from DataRepo.loaders.infusates_loader import InfusatesLoader
class TracersLoader(TableLoader):
DataColumnMetadata = DataTableHeaders(
NAME=TableColumn.init_flat(
...
# Cannot reference the InfusatesLoader here (to include the name of its
# sheet and its tracer name column) due to circular import
target_sheet=InfusatesLoader.DataSheetName,
source_column=InfusatesLoader.DataHeaders.TRACERNAME,
),
)
我现在可以通过在
tracers_loader.py
中设置静态字符串值来避免这个问题,但理想情况下,这些值只会存在于一个地方(每个值都在各自的类中)。
很多循环导入问题都与方法有关,所以我认为它们不适用于类属性?我尝试使用
importlib
并尝试在函数内进行导入,但是一旦它尝试设置类,我就会遇到导入错误。
循环导入问题的答案通常分为以下几类:
然而,“重新思考你的设计”通常不会伴随任何建议的设计模式。
显然,就我而言,这是一个设计问题。我知道情况很可能如此,但我只见树木不见森林。我坚持(正如 @user2357112 的评论所指出的)“单一事实来源”的概念。然而,我忽略了这样一个事实:我有一个与我正在建模的内容相符的概念选项。我的 Excel 文档中的每个工作表都有一个类,但我缺少文档本身的类,我可以在其中放置工作表间关系(以及文档中工作表列表的定义)。 @user2357112 将其称为“配置”,但我意识到这很容易成为一种“超类”或“协调类”。
我正要努力创建该类,但我确信这里需要一个定义表间关系的类。
我不知道如何称呼这样的设计模式,也不知道它会采取什么具体形式,但从概念上讲,这就是我所缺少的。
所以,举个例子,我需要的是这样的:
study_doc.py
:
class StudyDoc():
infusates_tracer_reference: {
"sheet": "Tracers",
"column": "Name",
}
tracers_infusate_reference: {
"sheet": "Infusates",
"column": "Tracer Name",
}
然后我可以将其导入到其他两个类中:
infusates_loader.py
:
from DataRepo.loaders.study_doc import StudyDoc
class InfusatesLoader(TableLoader):
DataColumnMetadata = DataTableHeaders(
TRACERNAME=TableColumn.init_flat(
...
source_sheet=StudyDoc.infusates_tracer_reference["sheet"],
source_column=StudyDoc.infusates_tracer_reference["column"],
),
)
tracers_loader.py
:
from DataRepo.loaders.study_doc import StudyDoc
class TracersLoader(TableLoader):
DataColumnMetadata = DataTableHeaders(
NAME=TableColumn.init_flat(
...
target_sheet=StudyDoc.tracers_infusate_reference["sheet"],
source_column=StudyDoc.tracers_infusate_reference["column"],
),
)
我可能会让它变得更复杂一些,但这是基本思想:所有这些关系都基于它们都属于同一个 Excel 文档的事实。该文档是关系的基础,因此它应该协调它们的连接。