0%

Fehler als Teil des Programmentwurfs · Abschlussprojekt

Abschlussprojekt: Projekt: Aussagekräftige Quizfehler entwerfen

Die Aussage dieses Kapitels in einem Satz: Ein Fehler gehört zu den Zusagen einer Methode und wird deshalb entworfen statt improvisiert.

Das Projekt führt alle Entscheidungen in einem kleinen Programm zusammen.

Der Entwurf

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

Drei Typen unter einer Basisklasse. Jeder wird an einer anderen Stelle abgefangen. Dieses Kriterium muss ein neuer Ausnahmetyp erfüllen.

Eingebaute Ausnahmen behalten ihre bisherigen Aufgaben. Ein falscher Typ ist ein TypeError. Das ist keine Bequemlichkeit: TypeError wird von allen verstanden, die Python programmieren. Ein privater Name dafür wäre schlechter.

Welche Entscheidungen die Bewertung prüft

EntscheidungWo
Ablehnen statt einen Wert erfindenquestion_at, respond
Eine eingebaute Ausnahme wählen, wenn eine passtDer TypeError für einen falschen Indextyp
Einen Fehler aus einer tieferen Ebene übersetzenload_points, unter Erhalt der Ursache
Werte als Attribute mitgebenQuestionNotFound, InvalidResponse
Nur an der Grenze abfangenrun_quiz, und nirgendwo sonst
Nicht verschlucken, was du nicht behandeln kannstDer Pfad für TypeError

Question.points_for löst eine Ausnahme aus, Attempt.record ruft es auf, und run_quiz ruft wiederum dieses auf. Warum hat nur run_quiz ein try?

Woran du ein gutes Ergebnis erkennst

Lies dein run_quiz und frage dich, was Lernende bei jeder Art von Fehler sehen:

  • Eine falsche Antwort bringt null Punkte und wird nicht erwähnt.

  • Eine leere Antwort wird übersprungen, mit einer Zeile, die die betroffene Frage nennt.

  • Eine fehlende Frage wird übersprungen, mit einer Zeile, die die Anzahl vorhandener Fragen nennt.

  • Ein fehlerhaft aufgebautes Quiz beendet den Lauf, weil es nichts gibt, womit er fortgesetzt werden könnte.

  • Ein Fehler in deinem eigenen Code führt zum Absturz, mit unverändertem Traceback.

Fünf verschiedene Ergebnisse, und keines davon lautet „Das Programm hat eine unbemerkt falsche Zahl ausgegeben“. Genau darum ging es in diesem Kapitel.

Aufgabe

Baue die Familie von Ausnahmen und das Programm, das sie verwendet.

QuizError(Exception) ist die Basisklasse. Darunter liegen QuestionNotFound(index, available) und InvalidResponse(prompt, response), die jeweils ihre Werte als Attribute festhalten und mit super().__init__ eine Meldung nach oben weiterreichen, sowie QuizConfigError, das keine Attribute benötigt.

Quiz.__init__ löst QuizConfigError aus, wenn keine Fragen übergeben werden: Ein Quiz ohne Inhalt lässt sich nicht durchführen und sollte deshalb gar nicht erst erstellt werden.

Quiz.question_at(index) löst TypeError aus, sofern der Typ des Index nicht exakt int ist, also auch bei True und False, und QuestionNotFound, wenn er außerhalb des Quiz liegt.

Quiz.points_for(index, response) löst bei einer leeren Antwort InvalidResponse aus, vergleicht die Antwort nach dem Entfernen umgebenden Leerraums und der Umwandlung in Kleinbuchstaben und gibt bei einer richtigen Antwort die Punkte zurück, bei einer falschen 0. Eine falsche Antwort ist kein Fehler.

load_points(raw) übersetzt den Fehler von int() in QuizError und erhält das Original als Ursache.

run_quiz(quiz, answers, log) ist die einzige Stelle, die Ausnahmen abfängt. Die Funktion gibt den Punktestand zurück. Jeder QuizError wird übersprungen und als "<TypeName>: <message>" aufgezeichnet. Ein TypeError wird genauso aufgezeichnet und dann erneut ausgelöst, denn ein Fehler im aufrufenden Code ist keine Ablehnung. Unabhängig vom Ergebnis hängt die Funktion mithilfe von finally zuletzt "answered N" an. Dabei zählt N die untersuchten Antwortpaare, einschließlich falscher Antworten, übersprungener Ablehnungen und des Paars, das einen TypeError auslöst. Paare nach diesem Fehler werden weder untersucht noch gezählt. Bei einer leeren Antwortsequenz wird "answered 0" aufgezeichnet.