Package: record-type
record-typeA proof-of-conceptrecordtype for Python.GoalsCreate a simple data type that's easy to explain to beginnersCreating the data type itself should be fastType annotations are supported, but not requiredInstances are immutable to make them (potentially) hashableSupport Python's entire parameter definition syntax for instance
instantiation, and do so idiomaticallySupport structural typing as much as possible (e.g., equality based on object
"shape" instead of inheritance)ExampleLet's say you're tracking items in your store. You may want to know an item's
name, price, and quantity on hand (this is an example from thedataclassesdocumentation).
That can be represented as a simple data class to store all that information
together.Therecordtype is meant to help facilitate creating such simple data classes:fromrecordsimportrecord@recorddefInventoryItem(name:str,price:float,*,quantity:int=0):"""Class for keeping track of an item in inventory."""This creates anInventoryItemclass whose call signature for intantiation
matches that of the function. Every parameter becomes the corresponding name of
an attribute that the argument gets assigned to. It also has:__slots__for performance__match_args__for pattern matching__annotations__for runtime type annotations__eq__()for equality__hash__()for hashing__repr__()which is suitable foreval()ImmutabilityCompared to other approachesdataclasses.dataclassYou can create adataclassfor this without much issue:fromdataclassesimportdataclass,KW_ONLY@dataclass(frozen=True,slots=True)classInventoryItem:"""Class for keeping track of an item in inventory."""name:strprice:float_:KW_ONLYquantity:int=0The drawbacks compared torecordare:The use ofKW_ONLYis awkwardIt requires using type annotationsTo make it immutable -- which implies being hashable -- and use__slots__requires remembering to opting in with the appropriate parametersNo support for*argsor**kwargsNamed tuplescollections.namedtupleUsingnamedtupleallows for a quick way to  create the class:fromcollectionsimportnamedtupleInventoryItem=namedtuple("InventoryItem",["name","price","quantity"])The drawbacks compared torecordare:Unable to support keyword-only, positional-only,*args, and**kwargsparametersNo support for type annotationsNo support for__match_args__Requires supporting both the attribute- and index-based APIs for any code
going forward that returns an instance of the classNo docstringtyping.NamedTupleYou can useNamedTupleto create a class that supports type annotations for a named tuple:fromtypingimportNamedTupleclassInventoryItem(NamedTuple):"""Class for keeping track of an item in inventory."""name:strprice:floatquantity:int=0The drawbacks compared torecordare:Unable to support keyword-only, positional-only,*args, and**kwargsparametersRequires type annotationsNo support for__match_args__Requires supporting both the attribute- and index-based APIs for any code
going forward that returns an instance of the classtypes.SimpleNamespaceYou can create a simple function that wrapsSimpleNamespacefromtypesimportSimpleNamespacedefInventoryItem(name:str,price:float,*,quantity:int=0):returnSimpleNamespace(name=name,price=price,quantity=quantity)The drawbacks compared torecordare:No support for__slots__No support for__match_args__No docstringNo runtime type annotationsMutable (and so no hashing)ManualYou can implement the equivalent ofrecordmanually:fromtypingimportAny,NoReturnclassInventoryItem:"""Class for keeping track of an item in inventory."""__slots__=("name","price","quantity")__match_args__=("name","price")name:strprice:floatquantity:intdef__init__(self,name:str,price:float,*,quantity:int=0)->None:object.__setattr__(self,"name",name)object.__setattr__(self,"price",price)object.__setattr__(self,"quantity",quantity)def__repr__(self)->str:returnf"{self.__class__.__name__}({self.name!r},{self.price!r}, quantity={self.quantity!r})"def__setattr__(self,_attr:Any,_val:Any)->NoReturn:raiseTypeError(f"{self.__class__.__name__}is immutable")def__eq__(self,other:Any)->bool:ifself.__slots__!=getattr(other,"__slots__",None):returnNotImplementedreturnall(getattr(self,attr)==getattr(other,attr)forattrinself.__slots__)def__hash__(self)->int:returnhash(tuple(self.name,self.price,self.quantity))The drawbacks compared torecordare:It's much more verbose to implement