Een eigen categorie en domeingrens
Laat CountError van ValueError afstammen. Maak require_count(number): geef nul en positieve gehele getallen terug; werp bij een negatief getal een echte CountError met bericht negatief aantal.
Een conversiefout en een ongeldig aantal kunnen allebei ValueError heten. Toch wil een aanroeper misschien alleen problemen met aantallen herstellen, en andere fouten laten opvallen. Een eigen fouttype maakt dat onderscheid expliciet, zonder berichten als protocol te hoeven ontleden.
Een klasse beschrijft een soort object. Voor een eenvoudig eigen fouttype heb je maar een kleine klassedefinitie nodig: class SettingError(ValueError): met daaronder een ingesprongen pass. pass vult het verplichte blok zonder extra handeling uit te voeren.
De nieuwe fout is een subklasse van ValueError. Via overerving krijgt zij het bestaande foutgedrag, waaronder het bewaren van een bericht. Je kunt dus meteen SettingError("ongeldige instelling") maken en dat object met raise werpen.
Voor deze les blijft de klasse bewust klein. Eigen attributen, methoden en uitgebreidere klassedefinities komen in de volgende module. Je gebruikt hier alleen de herkenbare categorie en het foutgedrag dat al bestaat.
De instellingenlezer vertaalt een mislukte conversie naar SettingError en bewaart de oorzaak. De buitenste handler vangt juist die eigen categorie af. Voorspel welke invoer een gewoon getal oplevert en welke invoer langs de handler gaat.
class SettingError(ValueError):
pass
def parse_timeout(text):
try:
return int(text)
except ValueError as problem:
raise SettingError("timeout is geen geheel getal") from problem
for text in ["12", "snel"]:
try:
print(parse_timeout(text))
except SettingError as error:
print("Afgewezen:", error)12 Afgewezen: timeout is geen geheel getal
De handler hoeft niet te zoeken naar een woord in het bericht om de categorie te herkennen. Het type draagt die betekenis. De tekst blijft bedoeld om de reden aan een mens uit te leggen.
Een handler voor ValueError past ook bij SettingError, omdat die daarvan afstamt. Dat is handig wanneer een buitengrens alle ongeldige waarden hetzelfde behandelt. Een plek die uitsluitend instellingsproblemen verwacht, kiest juist het specifiekere type.
class SettingError(ValueError):
pass
try:
raise SettingError("instelling ontbreekt")
except ValueError as error:
print("Brede handler:", error)
print(isinstance(error, SettingError))Brede handler: instelling ontbreekt True
Dit is dezelfde volgorderegel als bij IndexError en LookupError uit les 3. Een brede handler vóór een specifieke handler kan de eigen fout al onderscheppen. Alleen een nieuwe klassenaam toevoegen verandert die volgorde niet.
Met isinstance controleer je of een object bij een type of een daarvan afgeleid type hoort. isinstance(error, SettingError) is waar voor een gewone SettingError én voor een specifiekere subklasse. Een gewone ValueError hoort daardoor nog niet bij de eigen categorie.
Een vergelijking als type(error) is SettingError vraagt het precieze type en sluit verdere subklassen uit. Dat verschil is van belang bij de herkenningsfunctie in de opdracht. Die moet de hele eigen categorie accepteren, niet uitsluitend één exacte klasse.
Stel dat je eerst tekst omzet en daarna controleert of het aantal niet negatief is. Een brede except ValueError rond beide stappen vangt ook jouw eigen CountError uit de tweede stap. Je zou dan ten onrechte een geldig geschreven negatief getal als onleesbare tekst kunnen melden.
Houd daarom de conversie en de domeincontrole uit elkaar. Alleen een mislukte int wordt vertaald naar CountError("geen geheel getal") met de oorspronkelijke oorzaak. Een negatief getal krijgt zijn eigen reden en blijft die reden behouden.
De starter bevat CountError, maar de basisklasse moet nog passend worden gemaakt. Laat haar van ValueError afstammen en gebruik haar daarna voor de afgesproken fouten. Nul is geldig; negatieve aantallen zijn dat in deze oefening niet.
count_status ontvangt een functie als argument. Alleen jouw CountError levert de afgesproken afwijzingstekst op. Een gewone ValueError of een andere onverwachte fout moet zichtbaar blijven voor de aanroeper.
Laat CountError van ValueError afstammen. Maak require_count(number): geef nul en positieve gehele getallen terug; werp bij een negatief getal een echte CountError met bericht negatief aantal.
Maak parse_count(text): zet tekst om met int en weiger daarna negatieve aantallen als CountError met negatief aantal. Vertaal alleen een mislukte conversie naar CountError met geen geheel getal, vanuit de werkelijk opgevangen ValueError.
Maak is_count_error(error) die een echte boolean teruggeeft: True voor CountError en verdere subklassen daarvan, False voor gewone ValueError en andere fouten.
Maak count_status(operation): roep operation() aan. Geef bij succes goed: <waarde> terug en bij CountError ongeldig: <bericht>. Gewone ValueError en andere onverwachte fouten mogen niet door die handler verdwijnen.
isinstance herkent de categorie; houd conversie en domeincontrole gescheiden.Hierna bewaar je bruikbare informatie zonder onverwachte fouten stil te verbergen.
Voer je code uit en ontdek
wat je hebt gemaakt.
Je hoeft het niet in één keer goed te doen. Na het uitvoeren zie je hier welke stappen al kloppen.