-
Notifications
You must be signed in to change notification settings - Fork 0
Description
Use python dataclasses instead of dicts to store and use the various large sets of parameters required in the Hypnos workflow.
Documentation here: https://docs.python.org/3/library/dataclasses.html
Dataclasses provide a convenient place for us to document the required parameters and options in the dataclass docstring, as well as add type hints, in a much more convient format than the equivalent standard class object init we would need to write/maintain. Dataclasses can be quickly instantiated from dictionaries by unpacking (i.e. DataClass(**dict)) assuming the keys match the args.
For example:
@dataclass
class ComponentMaterials:
"""Dataclass for a given component's geometric parameters.
Parameters
----------
body : str
The body material. Can be either "steel" or "super steel".
widgets : str
The widget material. Can be one of "raspberry jam", "blackcurrant jam", or "strawberry jam".
"""
body: str
widgets: strAnother benefit is the __post_init__ method, which gives us a sensible place to compute derived parameters automatically upon instantiation. It is especially useful to do this when multiple components of an assembly can access the same dataclass instance for their parameters.
@dataclass
class MyAssemblyGeometry:
"""Dataclass for a given assembly's geometric parameters.
"""
radius: float
length: float
num_widgets: int
reticulate_splines: bool
def __post_init__(self):
"""Calculate derived parameters.
"""
self.widget_radius = self.radius/10Instantiating the classes would be relatively seamless from the JSON format, with something like this in the MyAssembly.__init__():
with open(json_file, "r") as file:
raw_data = file.read()
data = json.loads(raw_data)
materials_dict = data["materials"]
self.materials = MyAssemblyMaterials(**materials_dict)
geometry_dict = data["geometry"]
self.geometry = MyAssemblyGeometry(**geometry_dict)Or to go a step further, we could have the components/assemblies ask for their corresponding dataclasses as arguements, which would allow readers of the code to easily find the dataclass docstrings which describe the parameters required by a component. (This would also enable things like IDE autocompletion for developers).
The existing ease of instantiation from json could be maintained with a json classmethod which unpacks the file, and keeping dict as a valid arg type.
Something like:
# User's code
component_instance = MyAssembly.from_json(json_file)# Hypnos code
class MyAssembly(CreatedComponentAssembly):
def __init__(
materials: MyAssemblyMaterials | dict
geometry: MyAssemblyGeometry | dict
):
if isinstance(materials, dict):
materials = MyAssemblyMaterials(**materials)
if isinstance(geometry, dict):
geometry = MyAssemblyGeometry(**geometry)
super().__init__()
@classmethod
def from_json(cls, json_file):
with open(json_file, "r") as file:
raw_data = file.read()
data = json.loads(raw_data)
return cls(**data)An alternate version of the above code block, which avoids the variable type args would be:
# Hypnos code
class MyAssembly(CreatedComponentAssembly):
def __init__(
materials: MyAssemblyMaterials
geometry: MyAssemblyGeometry
):
super().__init__()
@classmethod
def from_json(cls, json_file):
with open(json_file, "r") as file:
raw_data = file.read()
data = json.loads(raw_data)
materials = data["materials"]
geometry = data["geometry"]
materials = MyAssemblyMaterials(**materials)
geometry = MyAssemblyGeometry(**geometry)
return cls(materials, geometry)