Package: typet
Types that make coding in Python quick and safe.Type[T] works best with Python 3.6 or later. Prior to 3.6, object types must
use comment type hint syntax.InstallationInstall it using pip:pip install typetFeaturesAn Object base class that eliminates boilerplate code and verifies and
coerces types when possible.Validation types that, when instantiated, create an instance of a specific
type and verify that they are within the user defined boundaries for the
type.Quick Start: Creating aPersonImport the Type[T] types that you will use.fromtypetimportBounded,Object,StringObject, for composing complex objectsBoundto describe a type that validates its value is of the correct type and within bounds upon instantiationString, which will validate that it is instantiated with a string with a length within the defined bounds.Create Type Aliases That Describe the Intent of the TypeAge=Bounded[int,0:150]Name=String[1:50]Hobby=String[1:300]In this example, aPersonhas anAge, which is an integer between 0 and
150, inclusive; aNamewhich must be a non-empty string with no more than
50 characters; and finally, aHobby, which is a non-empty string with no more
than 300 characters.Compose aPersonobject Using Type AliasesclassPerson(Object):name:Namesurname:Nameage:Agehobby:Hobby=NoneAssigning a class attribute sets that value as the default value for instances
of theObject. In this instance,hobbyis assigned a default value ofNone; by convention, this tells Python that the type isOptional[Hobby],
and Type[T] will allowNonein addition to strings of the correct length.Put It All TogetherfromtypetimportBounded,Object,StringAge=Bounded[int,0:150]Name=String[1:50]Hobby=String[1:300]classPerson(Object):name:Namesurname:Nameage:Agehobby:Hobby=NonePersonis now a clearly defined and typed object with an intuitive
constructor, hash method, comparison operators and bounds checking.Positional arguments will be in the order of the definition of class
attributes, and keyword arguments are also acceptable.jim=Person('Jim','Coder',23,'Python')bob=Person('Robert','Coder',hobby='C++',age=51)Python 2.7 to 3.5Type[T] supports PEP 484 class comment type hints for defining anObject.fromtypingimportOptionalfromtypetimportBounded,Object,StringAge=Bounded[int,0:150]Name=String[1:50]Hobby=String[1:300]classPerson(Object):name=None# type: Namesurname=None# type: Nameage=None# type: Agehobby=None# type: Optional[Hobby]Note that, because Python prior to 3.6 cannot annotate an attribute without
defining it, by convention, assigning the attribute toNonewill not imply
that it is optional; it must be specified explicitly in the type hint comment.ObjectTypesObjectOne of the cooler features of Type[T] is the ability to create complex
objects with very little code. The following code creates an object that
generates properties from the annotated class attributes that will ensure that
only values ofintor that can be coerced intointcan be set. It also
generates a full suite of common comparison methods.fromtypetimportObjectclassPoint(Object):x:inty:intPoint objects can be used intuitively because they generate a standard__init__method that will allow positional and keyword arguments.p1=Point(0,0)# Point(x=0, y=0)p2=Point('2',2.5)# Point(x=2, y=2)p3=Point(y=5,x=2)# Point(x=2, y=5)assertp1<p2# Trueassertp2<p1# AssertionErrorA close equivalent traditional class would be much larger, would have to be
updated for any new attributes, and wouldn’t support more advanced casting,
such as to types annotated using thetypingmodule:classPoint(object):def__init__(self,x,y):self.x=xself.y=ydef__repr__(self):return'Point(x={x}, y={y})'.format(x=self.x,y=self.y)def__setattr__(self,name,value):ifnamein('x','y'):value=int(value)super(Point,self).__setattr__(name,value)def__eq__(self,other):ifother.__class__isnotself.__class__:returnNotImplementedreturn(self.x,self.y)==(other.x,other.y)def__ne__(self,other):ifother.__class__isnotself.__class__:returnNotImplementedreturn(self.x,self.y)!=(other.x,other.y)def__lt__(self,other):ifother.__class__isnotself.__class__:returnNotImplementedreturn(self.x,self.y)<(other.x,other.y)def__le__(self,other):ifother.__class__isnotself.__class__:returnNotImplementedreturn(self.x,self.y)<=(other.x,other.y)def__gt__(self,other):ifother.__class__isnotself.__class__:returnNotImplementedreturn(self.x,self.y)>(other.x,other.y)def__ge__(self,other):ifother.__class__isnotself.__class__:returnNotImplementedreturn(self.x,self.y)>=(other.x,other.y)def__hash__(self):returnhash((self.x,self.y))Attributes can be declared optional either manually, by usingtyping.Optionalor by using the PEP 484 implicit optional of a default value ofNone.fromtypingimportOptionalfromtypetimportObjectclassPoint(Object):x:Optional[int]y:int=Nonep1=Point()# Point(x=None, y=None)p2=Point(5)# Point(x=5, y=None)StrictObjectBy default,Objectwill usecastfromtypingplusto attempt to coerce
any values supplied to attributes to the annotated type. In some cases, it may
be preferred to disallow casting and only allow types that are already of the
correct type.StrictObjecthas all of the features ofObject, but will not
coerce values into the annotated type.fromtypetimportStrictObjectclassPoint(StrictObject):x:inty:intPoint(0,0)# OkayPoint('2',2.5)# Raises TypeErrorStrictObjectusesis_instancefromtypingplusto check types, so it’s
possible to use types from thetypinglibrary for stricter checking.fromtypingimportListfromtypetimportStrictObjectclassIntegerContainer(StrictObject):integers:List[int]IntegerContainer([0,1,2,3])# OkayIntegerContainer(['a','b','c','d'])# Raises TypeErrorValidation TypesType[T] contains a suite of sliceable classes that will create bounded, or
validated, versions of those types that always assert their values are within
bounds; however, when an instance of a bounded type is instantiated, the
instance will be of the original type.BoundedBoundedcan be sliced with either two arguments or three. The first argument
is the type being bound. The second is aslicecontaining the upper and lower
bounds used for comparison during instantiation.fromtypetimportBoundedBoundedInt=Bounded[int,10:20]BoundedInt(15)# Okaytype(x)# <class 'int'>BoundedInt(5)# Raises ValueErrorOptionally, a third argument, a function, may be supplied that will be run on
the value before the comparison.fromtypetimportBoundedLengthBoundedString=Bounded[str,1:3,len]LengthBoundedString('ab')# OkayLengthBoundedString('')# Raises ValueErrorLengthBoundedString('abcd')# Raises ValueErrorLengthBecauselenis a common comparison method, there is a shortcut type,Lengththat takes two arguments and useslenas the comparison method.fromtypingimportListfromtypetimportLengthLengthBoundedList=Length[List[int],1:3]LengthBoundedList([1,2])# OkayLengthBoundedList([])# Raises ValueErrorLengthBoundedList([1,2,3,4])# Raises ValueErrorStringstrandlenare commonly used together so a special type,String, has
been added to simplify binding strings to specific lengths.fromtypetimportStringShortString=String[1:3]ShortString('ab')# OkayShortString('')# Raises ValueErrorShortString('abcd')# Raises ValueErrorNote that, on Python 2,Stringinstantiatesunicodeobjects and notstr.Metaclasses and UtilitiesSingletonSingletonwill cause a class to allow only one instance.fromtypetimportSingletonclassConfig(metaclass=Singleton):passc1=Config()c2=Config()assertc1isc2# OkaySingletonsupports an optional__singleton__method on the class that will
allow the instance to update if given new parameters.fromtypetimportSingletonclassConfig(metaclass=Singleton):def__init__(self,x):self.x=xdef__singleton__(self,x=None):ifx:self.x=xc1=Config(1)c1.x# 1c2=Config()# Okay because __init__ is not called.c2.x# 1c3=Config(3)# Calls __singleton__ if it exists.c1.x# 3c2.x# 3c3.x# 3assertc1isc2isc3# Okay@singletonAdditionally, there is a decorator,@singletonthat can be used make a class
a singleton, even if it already uses another metaclass. This is convenient for
creating singleton Objects.fromtypetimportObject,singleton@singletonclassConfig(Object):x:intc1=Config(1)c2=Config()# Okay because __init__ is not called.assertc1isc2# Okay@metaclassType[T] contains a class decorator,@metaclass, that will create a derivative
metaclass from the given metaclasses and the metaclass used by the decorated
class and recreate the class with the derived metaclass.Most metaclasses are not designed to be used in such a way, so careful testing
must be performed when this decorator is to be used. It is primarily intended
to ease use of additional metaclasses with Objects.fromtypetimportmetaclass,Object,Singleton@metaclass(Singleton)classConfig(Object):x:intc1=Config(1)c2=Config()# Okay because __init__ is not called.assertc1isc2# Okay