Python:数据类的循环依赖/前向变量声明?

gma*_*dau 6 python forward-declaration circular-reference python-3.x python-dataclasses

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

@dataclass
class A:
    children: List[B]

@dataclass
class B:
    parent: A
Run Code Online (Sandbox Code Playgroud)

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

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

文件objects_A

import objects_B

obj_a1 = A(
    children=[
        objects_B.obj_b1,
        objects_B.obj_b2
    ]
)
Run Code Online (Sandbox Code Playgroud)

文件objects_B

import objects_A

obj_b1 = B(
    parent=objects_A.obj_a1
)

obj_b2 = B(
    parent=objects_A.obj_a1
)
Run Code Online (Sandbox Code Playgroud)

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

目前,我设置parentNone(针对类型提示),然后循环进行设置obj_a1

for obj_b in obj_a1.children:
    obj_b.parent = obj_a1
Run Code Online (Sandbox Code Playgroud)

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

小智 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)
Run Code Online (Sandbox Code Playgroud)

这会起作用。我已经children: List[object]在 中添加了ParentBase. 这对于此代码的工作来说并不是必需的,但如果您添加它,您的 IDE 可以在您开始访问child.parent..

这不会阻止任何人用 a 实例化Childa ParentBase。为了解决这个问题,你可以这样做:

@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)
Run Code Online (Sandbox Code Playgroud)

它可能过于防御性且样板代码过多。是否想让它更安全取决于您的团队的偏好。