2.1 Validator class A validator is a descriptor for managed attribute access. Prior to storing any data, it verifies that the new value meets various type and range restrictions. If those restrictions aren’t met, it raises an exception to prevent data corruption at its source. This$_{Validator}$class is both an abstract base class and a managed attribute descriptor: from abc import ABC, abstractmethod classValidator (ABC): def __set_name__ (self, owner, name): self.private_name='_'+name def __get__ (self, obj, objtype = None ): return getattr(obj,self.private_name) def __set__ (self, obj, value): self.validate(value) setattr(obj,self.private_name, value) @abstractmethod def validate(self, value): pass classOneOf (Validator): def __init__ (self,*options): self.options =set(options) def validate(self, value): if value not in self.options: raise ValueError(f'Expected { value !r} to be one of { self.options !r} ') classNumber (Validator): def __init__ (self, minvalue = None , maxvalue = None ): self.minvalue=minvalue self.maxvalue=maxvalue def validate(self, value): if not isinstance(value, ( int,float)): raise TypeError(f'Expected { value !r} to be an int or float ') if self.minvalue is not None and value<self.minvalue: raise ValueError( f'Expected { value !r} to be at least { self.minvalue !r} ' (continues on next page) 7 2.2 Custom validators Custom validators need to inherit from$_{Validator}$ and must supply a$_{validate()}$ method to test various re- strictions as needed. Here are three practical data validation utilities: 3) $_{String}$verifies that a value is a$_{str}$ . Optionally, it validates a given minimum or maximum length. It can validate a user-defined predicate as well. 2) $_{Number}$verifies that a value is either an$_{int}$ or$_{float}$ . Optionally, it verifies that a value is between a given minimum or maximum. 1) $_{OneOf}$verifies that a value is one of a restricted set of options.