Python dataclass: Forcing a dictionary field to be a deep copy
Question:
I am working with a dataclass that contains a dict.
I want the dict to be a deepcopy, without having to rely on a post_init call, which would basically void to interest of a dataclass
What would be a nice solution ?
from dataclasses import dataclass, field
from typing import Dict
@dataclass
class ClassWithDict:
the_dict: Dict = field(default_factory=dict, kw_only=True)
toto = {"toto": "tata"}
the_class = ClassWithDict(the_dict=toto)
assert toto == the_class.the_dict
assert toto is not the_class.the_dict # FALSE
Answers:
Solution, if not wanting to use a __post__init__
using the object.setattr
method to force a copy once the object is initialized, is to use a metaclass
:
import copy
from dataclasses import dataclass, field
from typing import Dict
class DataClassWithDeepCopyMeta(type):
def __call__(cls, *args, **kwargs):
args = copy.deepcopy(args)
kwargs = copy.deepcopy(kwargs)
return super().__call__(*args, **kwargs)
@dataclass
class ClassWithDict(metaclass=DataClassWithDeepCopyMeta):
the_dict: Dict = field(default_factory=dict)
toto = {"toto": "tata"}
the_class = ClassWithDict(toto)
assert toto == the_class.the_dict
assert toto is not the_class.the_dict
I am working with a dataclass that contains a dict.
I want the dict to be a deepcopy, without having to rely on a post_init call, which would basically void to interest of a dataclass
What would be a nice solution ?
from dataclasses import dataclass, field
from typing import Dict
@dataclass
class ClassWithDict:
the_dict: Dict = field(default_factory=dict, kw_only=True)
toto = {"toto": "tata"}
the_class = ClassWithDict(the_dict=toto)
assert toto == the_class.the_dict
assert toto is not the_class.the_dict # FALSE
Solution, if not wanting to use a __post__init__
using the object.setattr
method to force a copy once the object is initialized, is to use a metaclass
:
import copy
from dataclasses import dataclass, field
from typing import Dict
class DataClassWithDeepCopyMeta(type):
def __call__(cls, *args, **kwargs):
args = copy.deepcopy(args)
kwargs = copy.deepcopy(kwargs)
return super().__call__(*args, **kwargs)
@dataclass
class ClassWithDict(metaclass=DataClassWithDeepCopyMeta):
the_dict: Dict = field(default_factory=dict)
toto = {"toto": "tata"}
the_class = ClassWithDict(toto)
assert toto == the_class.the_dict
assert toto is not the_class.the_dict