Search code examples
pythonpython-3.xcircular-dependencypython-dataclasses

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 in this case because the firstly-defined class can use the secondly-defined class as usual, even in case/data classes.

However in Python, the following code

class A:
    b = B()

class B:
    a = A()     

throws a compile error because class B is not defined when class A is being defined.

You can work around this simple case, like in this answer

class A:
    pass

class B:
    a = A()

A.b = B()

However, this way does not work for data classes in Python because assigning members after the definition of data classes will not update the auto-generated methods of the data classes, which makes the usage of "data class" useless.

@dataclass
class A:
    b: B  # or `b: Optional[B]`

@dataclass
class B:
    a: A  # or `a: Optional[A]`

How can I avoid this problem?


Solution

  • 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:

    class A:
        pass
    
    @dataclass
    class B:
        a: A
    
    @dataclass
    class A:
        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
    
    @dataclass
    class B:
        a: A
    
    @dataclass
    class A:
        b: B