Skip to content Skip to sidebar Skip to footer

How To Define Circularly Dependent Data Classes In Python 3.7+?

Suppose that class A has a member whose type is class B, and class B has a member whose type is class A. In Scala or Kotlin, you can define the classes in any order without worries

Solution 1:

There are several ways to solve circular dependencies like this, see Type hints: solve circular dependency

You can always apply the decorator manually (and update the annotations), like @Nearoo's answer shows.

However, it might be easier to "forward declare" the class:

classA:
    pass@dataclassclassB:
    a: A@dataclassclassA:
    b: B

Or simply use a forward reference:

@dataclass
class B:
    a: 'A'@dataclass
class A:
    b: B

The cleanest is to import Python 4.0's behavior (if you can):

from __future__ import annotations

@dataclassclassB:
    a: A@dataclassclassA:
    b: B

Solution 2:

You can achieve your goal by applying the dataclass decorator only after we injected the field b into A. For that, we simply have to add the type annotation into A's __annotations__-field

The following code solves your problem:

classA:
    b: None# Note: __annotations__ only exists if >=1 annotation exists@dataclassclassB:
    a: A

A.__annotations__.update(b=B) # Note: not the same as A.b: B
A = dataclass(A) # apply decorator

Concerning the safety and validity of this method, PEP 524 states that

..at the module or class level, if the item being annotated is a simple name, then it and the annotation will be stored in the __annotations__ attribute of that module or class. [This attribute] is writable, so this is permitted:

__annotations__['s'] = str

So adding a type annotation later on by editing __annotations__ is identical to defining it at the class definition.

Solution 3:

As python is script language - there is no way to do it with @dataclass. Because there is no "autowired" (dependency injection) mechanism in python. At this moment if you need circular dependency - you should use one of class as regular.

classA:
    b = None@dataclassclassB:
    a: A

a = A()
a.b = B(a)

Python compiler goes through each line, without jumping from class/function definition. And when compiler/interpreter see following line b: B and it didn't see B class before - it will throw an exception NameError: name 'B' is not defined

I would like to believe that there is way to do that(circular dependency for @dataclass), but the truth is cruel. (There are many things that you can do in Java/other language and can't do in python. Another direction of this statement is truthful either.)

Post a Comment for "How To Define Circularly Dependent Data Classes In Python 3.7+?"