Cyclic or “Recursive” Dataclasses¶
Note
Important: The current functionality for cyclic or “recursive” dataclasses is being re-imagined. Please refer to the new docs for V1 Opt-in features, which introduces enhanced support for these use cases. For more details, see the Field Guide to V1 Opt‐in and the Recursive Types and Dataclasses with Cyclic References in V1 documentation.
This change is part of the ongoing improvements in version v0.34.0+
, and the old functionality will no longer be maintained in future releases.
Prior to version v0.27.0
, dataclasses with cyclic references
or self-referential structures were not supported. This
limitation is shown in the following toy example:
from dataclasses import dataclass
from dataclass_wizard import JSONWizard
@dataclass
class A(JSONWizard):
a: 'A | None' = None
a = A.from_dict({'a': {'a': {'a': None}}})
assert a == A(a=A(a=A(a=None)))
This has been a longstanding issue.
New in v0.27.0
: The Dataclass Wizard now extends its support
to cyclic and self-referential dataclass models.
The example below demonstrates recursive dataclasses with cyclic
dependencies, following the pattern A -> B -> A -> B
.
With Class Inheritance¶
Here’s a basic example demonstrating the use of recursive dataclasses
with cyclic dependencies, using a class inheritance model and
the JSONWizard
mixin:
from __future__ import annotations # This can be removed in Python 3.10+
from dataclasses import dataclass
from dataclass_wizard import JSONWizard
@dataclass
class A(JSONWizard):
class _(JSONWizard.Meta):
# enable support for self-referential / recursive dataclasses
recursive_classes = True
b: 'B | None' = None
@dataclass
class B:
a: A | None = None
# confirm that `from_dict` with a recursive, self-referential
# input `dict` works as expected.
a = A.from_dict({'b': {'a': {'b': {'a': None}}}})
assert a == A(b=B(a=A(b=B())))
Without Class Inheritance¶
Here is the same example as above, but with relying solely on dataclasses
, without
using any special class inheritance model:
from __future__ import annotations # This can be removed in Python 3.10+
from dataclasses import dataclass
from dataclass_wizard import fromdict, LoadMeta
@dataclass
class A:
b: 'B | None' = None
@dataclass
class B:
a: A | None = None
# enable support for self-referential / recursive dataclasses
LoadMeta(recursive_classes=True).bind_to(A)
# confirm that `from_dict` with a recursive, self-referential
# input `dict` works as expected.
a = fromdict(A, {'b': {'a': {'b': {'a': None}}}})
assert a == A(b=B(a=A(b=B())))