A Python dataclass implementation based on MutableDict. It shares some features
with builtin typing.TypedDict, but can be extended. It's extendable
by default. The other inspiration is the attrs pip package, especially
the @frozen decorator.
Compared to other implementations strongly typed tree hierachy works
out of the box, which inspires the class name, Node, like a tree's node,
and its alias, DataClass.
Unlike in a simple dict a missing entry causes AttributeError
instead of KeyError.
The most basic usage is to use as an enhanced dict:
from dewi_dataclass import DataClass
val = DataClass()
# use as regular dict
val['key1'] = 42
# use as class member
print(val.key1) # 42
# missing item raises AttributeError
print(val['missing'])
print(val.missing)The class' features come live when a subclass is created.
It has a factory method, create which checks if
the members are valid, already known or not:
class Point(DataClass):
x: int
def __init__(self):
self.y = 4
print(Point.create(x=3))
print(Point.create(x=3, y=22))
# and raises AttributeError if the field is unknown
print(Point.create(z=1))If the member type's constructor (or no-arg __init__ method) is just fine,
the data class' __init__ is not needed at all:
class Data(DataClass):
x: int
y: list[str]
d = Data()
print(d.y) # []
# but d.x is not yet set as it's not yet read.A specialized list type is also provided for lists of DataClass objects.
from data_class import DataClass, DataList
class Modules(DataClass):
# details are omitted
pass
class Params(DataClass):
modules: DataList[Modules] # and not list[Modules]DataList (or NodeList) is useful when a JSON or YAML file is loaded and its content should be loaded into a data class in a typesafe way.
class Params(DataClass):
...
params = Params()
params.load_from(yaml.safe_load(...))The above example can be shortened if the variable can be created at the same time:
params = Params.create(**yaml.safe_load(...))Any data class can be frozen, new members cannot be defined even
in the __init__() method, only via annotations or create(),
but the class can be extended via inheritance in a sublcass.
@frozen
class Point(DataClass):
x: float
y: float
def __init__(self)
self.z = 42 # ❌ doesn't work, raises AttributeError
class Point3D(Point):
def __init__(self):
self.z = 0.0 # ✅ OK
class Point3Dv2(Point):
z: float # ✅ OK
point = Point()
point.z = 0.0 # ❌ doesn't work, raises AttributeErrorFor YAML / JSON serialization either the types should be registered or in this case a dedicated method is required to convert the dict-like and list-like objects to actual dicts and lists.
As the top-level data structure is a specialized dictionary, as_dict() can be used:
from dewi_dataclass import DataClass, as_dict
class Point(DataClass):
...
point = Point()
print(point.as_dict())
# or
print(as_dict(point))