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?
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)