【问题标题】:Python: Circular dependency of dataclasses / Forward variable declaration?Python:数据类的循环依赖/前向变量声明?
【发布时间】:2020-04-29 22:21:45
【问题描述】:

所以,我在一个文件中有这两个数据类:

@dataclass
class A:
    children: List[B]

@dataclass
class B:
    parent: A

,这可以通过使用__future__.annotations 功能实现。

然后我有另外两个文件,每个文件都有一堆对象,每种类型对我的项目来说都是静态的。

文件objects_A:

import objects_B

obj_a1 = A(
    children=[
        objects_B.obj_b1,
        objects_B.obj_b2
    ]
)

文件objects_B:

import objects_A

obj_b1 = B(
    parent=objects_A.obj_a1
)

obj_b2 = B(
    parent=objects_A.obj_a1
)

显然,文件之间存在循环依赖问题,但即使它们在同一个文件中也不起作用,因为一种类型的变量依赖于另一种类型的变量才能成功。
在obj_a1 中初始化B 对象也不起作用,因为这里没有self 的概念。

目前,我正在将parent 设置为None(针对类型提示),然后在obj_a1 上循环进行设置:

for obj_b in obj_a1.children:
    obj_b.parent = obj_a1

各位有什么好主意吗?
不知道它是否有帮助,但是这些对象是静态的(在这些声明之后它们不会改变)并且它们具有某种父子关系(您肯定已经注意到了)。
如果可能的话,我希望将每种类型的变量放在不同的文件中。

【问题讨论】:

  • 您本质上是在将一个对象映射到另一个对象,为什么不能制作某种字典?例如:dict = {PARENT_OBJ: [CHILDREN]}
  • 这个想法是在项目周围传递一个B 类型的对象,并且能够从父A 访问其他内容,而无需使用其他结构,如字典。
  • 没有办法让实际的对象引用具有循环依赖关系。您可以通过最终实现的映射获得间接引用,正如 Peter S 建议的那样,或者您使用空的子列表初始化所有 As,并且仅在其对象文件为时将 Bs 添加到关系中执行。这两个选项都不起作用吗?

标签: python python-3.x forward-declaration circular-reference python-dataclasses


【解决方案1】:

解决此问题的一种方法是使用基类作为前向声明的替代方案。

@dataclass
class ParentBase:
    children: List[object]


@dataclass
class Child:
    parent: ParentBase


@dataclass
class Parent(ParentBase):
    children: List[Child]



parent = Parent(children=[])
child = Child(parent=parent)
parent.children.append(child)

这会奏效。我在ParentBase 中添加了children: List[object]。这不是此代码工作所必需的,但如果您添加它,您的 IDE 可以在您开始访问 child.parent. 时为您提供帮助。

这将不会阻止任何人使用 ParentBase 实例化 Child。要解决这个问题,你可以这样做:

@dataclass
class ParentBase(abc.ABC):
    children: List[object]

    @abc.abstractmethod
    def __post_init__(self):
        pass


@dataclass
class Child:
    parent: ParentBase


@dataclass
class Parent(ParentBase):
    children: List[Child]

    def __post_init__(self):
        pass


parent = Parent(children=[])
child = Child(parent=ParentBase())  # TypeError
child = Child(parent=parent)
parent.children.append(child)

它可能过于防御和过多的样板代码。是否要使其更安全取决于您团队的偏好。

【讨论】:

    【解决方案2】:

    我知道我迟到了,但我会把我的答案留在这里供其他人使用。

    根据PEP 563,python 3.7 引入了注解的惰性求值,这在循环依赖的情况下非常有用。

    @dataclass
    class StudentData:
        school: 'SchoolData'
    
    @dataclass
    class SchoolData:
        students: StudentData
    

    如您所见,SchoolData 类型注释包含在引号内,允许您在声明之前引用 SchoolData 类型。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-10-08
      • 2012-04-24
      • 2010-12-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-02
      相关资源
      最近更新 更多