Differences with dataclasses
While this project doesn’t intend to exactly replicate other similar modules it’s worth noting where they differ in case users get tripped up.
Prefabs don’t behave quite the same (externally) as dataclasses. They are very different internally.
This doesn’t include things that haven’t been implemented, and only focuses on intentional differences. Unintentional differences may be patched or will be added to this list.
Functional differences
prefabs do not generate the comparison methods other than
__eq__.This could be added fairly easily but I don’t use this feature so it’s not a priority.
the
as_dictmethod inprefab_classesdoes not behave the same as dataclasses’asdict.as_dictdoes not deepcopy the included fields, modification of mutable fields in the dictionary will modify them in the original object.as_dictdoes not recurseRecursion would require knowing how other objects should be serialized.
dataclasses
asdict’s recursion appears to be for handling json serialization prefab_classes provides ato_jsonfunction to assist with that.
dataclasses provides a
fieldsfunction to access the underlying fields.Once a prefab class has been generated the underlying ‘recipe’ code is removed as much as possible.
Prefab classes provide a
PREFAB_FIELDSattribute with the field names in order.The dynamic classes generate their code lazily so they need to keep the ‘recipe’ details around.
__prefab_internals__contains this information.
Plain
attribute(...)declarations can be used without the use of type hints.If a plain assignment is used, all assignments must use
attribute.
Post init processing uses
__prefab_post_init__instead of__post_init__This is just a case of not wanting any confusion between the two.
attrssimilarly does__attrs_post_init__.__prefab_pre_init__can also be used to define something to run before the body of__init__.If an attribute name is provided as an argument to either the pre_init or post_init functions the value will be passed through.
Unlike dataclasses, prefab classes will let you use unhashable default values.
This isn’t to say that mutable defaults are a good idea in general but prefabs are supposed to behave like regular classes and regular classes let you make this mistake.
Usually you should use
attribute(default_factory=list)or similar.
If
initisFalsein@prefab(init=False)the method is still generated but renamed to__prefab_init__.Slots are supported but only via declaring a class with
__slots__ = SlotAttributes(...)The support for slots in
attrsanddataclassesinvolves recreating the class as it is not possible to effectively define__slots__after class creation. This can cause bugs where decorators or caches hold references to the original class.By requiring slotted classes to be declared this way
@prefabdoes not need to create a new class so all references are still correct.
InitVar annotations are not supported.
Passing arguments to
__prefab_post_init__is done by adding the argument to the method signature.Assignment is automatically skipped for any such values, default factories will be called and passed to the post init method.
To exclude such values from the fields list and other magic methods set
exclude_field=Trueas an argument toattribute. Such attributes are required to be arguments to__prefab_post_init__.
The
__repr__method for prefabs will have a different output if it will notevalcorrectly.This isn’t a guarantee that the regular
__repr__will eval, but if it is known that the output would notevalthen an alternative repr is used which does not look like it wouldeval.
default_factory functions will be called if
Noneis passed as an argumentThis makes it easier to wrap the function.