0%

Fouten als onderdeel van programmaontwerp · eindproject

Eindproject: Project: ontwerp betekenisvolle quizfouten

Het betoog van dit hoofdstuk in één regel: een fout maakt deel uit van wat een methode belooft en wordt dus ontworpen in plaats van geïmproviseerd.

Het project brengt elke beslissing samen in één klein programma.

Het ontwerp

QuizError                 the base: anything this program refuses
  QuestionNotFound        no question at that position
  InvalidResponse         the answer could not be used
  QuizConfigError         the quiz itself is malformed

Drie typen onder één basis. Elk wordt ergens anders opgevangen, de toets waaraan een nieuw exceptietype moet voldoen.

Ingebouwde typen behouden hun bestaande taken. Een verkeerd type is een TypeError. Dat is geen luiheid; elke Pythonprogrammeur begrijpt TypeError en een eigen naam ervoor zou slechter zijn.

De beslissingen die de beoordelaar controleert

BeslissingWaar
Weigeren in plaats van een waarde verzinnenquestion_at, respond
Een ingebouwd type kiezen als dat al pastde TypeError voor een ongeldige index
Een fout van een lager niveau vertalenload_points, met behoud van de oorzaak
De waarden als attributen meedragenQuestionNotFound, InvalidResponse
Alleen bij de grens opvangenrun_quiz, en nergens anders
Niet inslikken wat je niet kunt afhandelende TypeError-route

Question.points_for gooit een exceptie op, Attempt.record roept die aan en run_quiz roept dat weer aan. Waarom heeft alleen run_quiz een try?

Hoe goed werk eruitziet als je klaar bent

Lees je run_quiz en vraag wat een cursist bij elk soort fout ziet:

  • een onjuist antwoord scoort nul en wordt niet genoemd;

  • een leeg antwoord wordt overgeslagen met een regel die zegt om welke vraag het gaat;

  • een ontbrekende vraag wordt overgeslagen met een regel die zegt hoeveel er zijn;

  • een ongeldige quiz stopt de uitvoering, omdat er niets is om mee verder te gaan;

  • een fout in je eigen code crasht, met de traceback intact.

Vijf verschillende uitkomsten en geen ervan is ‘het programma drukte een getal af dat ongemerkt verkeerd was’. Daarvoor diende dit hoofdstuk.

Opdracht

Bouw de exceptiefamilie en het programma dat die gebruikt.

QuizError(Exception) is de basis. Daaronder: QuestionNotFound(index, available) en InvalidResponse(prompt, response), die elk hun waarden als attributen bewaren en een bericht naar boven doorgeven met super().__init__, en QuizConfigError, dat geen attributen nodig heeft.

Quiz.__init__ gooit QuizConfigError op als die geen vragen krijgt: een quiz zonder inhoud kan niet worden uitgevoerd en hoort dus niet te worden gemaakt.

Quiz.question_at(index) gooit TypeError op tenzij het type van de index exact int is, ook bij True en False, en QuestionNotFound als die buiten de quiz ligt.

Quiz.points_for(index, response) gooit InvalidResponse op voor een leeg antwoord, vergelijkt het antwoord na het verwijderen van witruimte aan de uiteinden en het omzetten naar kleine letters, geeft de punten terug voor een juist antwoord en 0 voor een onjuist antwoord. Een onjuist antwoord is geen fout in het programma.

load_points(raw) vertaalt de fout van int() naar QuizError en behoudt het origineel als oorzaak.

run_quiz(quiz, answers, log) is de enige plek die opvangt. Die geeft de score terug. Elke QuizError wordt overgeslagen en geregistreerd als "<TypeName>: <message>". Een TypeError wordt op dezelfde manier geregistreerd en daarna opnieuw opgegooid, omdat een fout in de aanroepende code geen weigering is. Wat er ook gebeurt, de functie voegt met finally als laatste "answered N" toe. Hier telt N de onderzochte antwoordparen, inclusief onjuiste antwoorden, overgeslagen weigeringen en het paar dat een TypeError opgooit. Paren na die fout worden niet onderzocht of geteld; een lege antwoordreeks registreert "answered 0".