Search code examples
pythonnestedinitializationreinitialization

Reinitialization of dataclass object does not reinitialize nested dataclass objects


I'm having trouble reinitializing nested dataclass object.

I have 2 nested dataclasses set up like so:

from dataclasses import dataclass, field
from typing import Dict

@dataclass
class MyNestedClass():
    field1: str = ""
    field2: int = 0
    field3: Dict[str, float] = field(default_factory=dict)

@dataclass
class MyClass():
    field1: str = ""
    field2: int = 0
    field3: Dict[str, float] = field(default_factory=dict)
    field4: MyNestedClass = MyNestedClass()

As part of a unit testing procedure, I would like to initialize a new MyClass object and then run a test. However, I've noticed something odd. When I reinitialize my MyClass object, it somehow remembers the contents of field4. For example:

my_obj = MyClass()
print(my_obj)

my_obj.field2 = 1
my_obj.field3 = {'something': 2.5}
my_obj.field4.field1 = "B"
my_obj.field4.field2 = 2
my_obj.field4.field3 = {'something else': 3.14}
print(my_obj)

my_obj = MyClass()
print(my_obj)

yields

MyClass(field1='', field2=0, field3={}, field4=MyNestedClass(field1='', field2=0, field3={}))
MyClass(field1='', field2=1, field3={'something': 2.5}, field4=MyNestedClass(field1='B', field2=2, field3={'something else': 3.14}))
MyClass(field1='', field2=0, field3={}, field4=MyNestedClass(field1='B', field2=2, field3={'something else': 3.14}))

does the autogenerated dataclass constructor for MyClass not call the constructor for MyNestedClass by default? If not, what is it doing instead and what is the correct way to do this?


Solution

  • As Michael points out, the constructor generated by the @dataclass decorator includes a mutable default argument so that an instance of MyNestedClass is created only once when the MyClass is defined and not uniquely for each instantiation.

    Defining MyClass using a field resolves this problem:

    @dataclass
    class MyClass():
        field1: str = ""
        field2: int = 0
        field3: Dict[str, float] = field(default_factory=dict)
        field4: MyNestedClass = field(default_factory=MyNestedClass)