Package: prefab-classes
PrefabClasses - Python Class Boilerplate GeneratorWrites the class boilerplate code so you don't have to.
Yet another variation on attrs/dataclasses.Unlikedataclassesorattrs,prefab_classeshas a
focus on performance and startup time in particular.
This includes trying to minimise the impact of importing
the module itself.Classes are written lazily when you first access the methods or
eagerly when the module is compiled into a .pyc or rewritten out
to a new .py source file.The dynamic method of evaluating lazily is more flexible, while
the compiled method is faster (once the .pyc file has been generated).For more detail look at thedocumentation.UsageDefine the class using plain assignment andattributefunction calls:fromprefab_classesimportprefab,attribute@prefabclassSettings:hostname=attribute(default="localhost")template_folder=attribute(default='base/path')template_name=attribute(default='index')Or with type hinting:fromprefab_classesimportprefab@prefabclassSettings:hostname:str="localhost"template_folder:str='base/path'template_name:str='index'In either case the result behaves the same.>>>fromprefab_classes.funcsimportto_json>>>s=Settings()>>>print(s)Settings(hostname='localhost',template_folder='base/path',template_name='index')>>>to_json(s)'{"hostname": "localhost", "template_folder": "base/path", "template_name": "index"}'For further details see theusagepages in the documentation.Why not just use attrs/dataclasses?If attrs or dataclasses solves your problem then you should use them.
They are thoroughly tested, well supported packages. This is a new
project and has not had the rigorous real world testing of either
of those.Dataclasses/attrs/pydantic all impose some overhead on startup time.
Prefab classes aims to minimise startup time and performance impact
of class generation and in doing so sacrifices some features or
nice internals of these other implementations.Import time example:CommandMean [ms]Min [ms]Max [ms]Relativepython -c "pass"26.6 ± 1.225.030.61.00python -c "import prefab_classes"28.4 ± 0.427.629.51.07 ± 0.05python -c "import dataclasses"48.4 ± 1.046.551.51.82 ± 0.09python -c "import attrs"67.3 ± 0.765.971.12.53 ± 0.11python -c "import pydantic"105.4 ± 3.4100.3115.73.96 ± 0.22For more detailed tests you can look at theperformance section of the docs.How does it workThe@prefabdecorator either rewrites the class dynamically, putting methods
in place that will be generated as they are first accessedORit acts
as a marker to indicate the class should be transformed for the compiled
classes.Compiled classes can both be imported directly or converted back to new .py
files. Direct import will perform the conversion before creating the .pyc file.example.py# COMPILE_PREFABSfromprefab_classesimportprefab,attributefrompathlibimportPath@prefab(compile_prefab=True)classSettingsPath:hostname=attribute(default="localhost")template_folder=attribute(default='base/path')template_name=attribute(default='index')file_types=attribute(default_factory=list)def__prefab_post_init__(self,template_folder,file_types):self.template_folder=Path(template_folder)file_types.extend(['.md','.html'])self.file_types=file_typesDirect import using prefab_compilerfromprefab_classesimportprefab_compilerwithprefab_compiler():fromexampleimportSettingsPath# Use normally from hereCompile to a new .py file using rewrite_to_py:>>>fromprefab_classes.compiledimportrewrite_to_py>>>rewrite_to_py('example.py','example_compiled.py',use_black=True)Using black to format for ease of reading.example_compiled.py# DO NOT MANUALLY EDIT THIS FILE# MODULE: example_compiled.py# GENERATED FROM: example.py# USING prefab_classes VERSION: v0.9.1frompathlibimportPathclassSettingsPath:COMPILED=TruePREFAB_FIELDS=["hostname","template_folder","template_name","file_types"]__match_args__=("hostname","template_folder","template_name","file_types")def__init__(self,hostname="localhost",template_folder="base/path",template_name="index",file_types=None,):self.hostname=hostnameself.template_name=template_namefile_types=file_typesiffile_typesisnotNoneelselist()self.__prefab_post_init__(template_folder=template_folder,file_types=file_types)def__repr__(self):returnf"{type(self).__qualname__}(hostname={self.hostname!r}, template_folder={self.template_folder!r}, template_name={self.template_name!r}, file_types={self.file_types!r})"def__eq__(self,other):return((self.hostname,self.template_folder,self.template_name,self.file_types)==(other.hostname,other.template_folder,other.template_name,other.file_types,)ifself.__class__==other.__class__elseNotImplemented)def__prefab_post_init__(self,template_folder,file_types):self.template_folder=Path(template_folder)file_types.extend([".md",".html"])self.file_types=file_typesIfcompile_plain=Trueis provided as an argument to@prefabtheCOMPILEDandPREFAB_FIELDvariables will not be set on the class.Creditautogenfunction and some magic method definitions taken fromDavid Beazley's CluegenGeneral design based on previous experience usingdataclassesandattrsand trying to match the
requirements forPEP 681.