0%

Dataclasses und Wertobjekte · Abschlussprojekt

Abschlussprojekt: Projekt: Ergebnisdatensätze und Konfiguration modellieren

Die Aussage dieses Kapitels passt in einen Satz: Lass Python den Code schreiben, der bereits aus deiner Attributliste folgt, und schreibe weiterhin den Code, der eine Entscheidung ausdrückt.

Das Projekt ist ein kleines Ergebnissystem, in dem jede Entscheidung des Kapitels einmal vorkommt.

Was du baust

QuizConfig    how a quiz runs         changeable, has defaults
Points        an amount               frozen value
Question      a question              frozen value, built several ways
ResultRecord  one finished result     frozen value
Gradebook     the collection          entity, changes as results arrive

Drei eingefrorene Werte, eine veränderbare Konfiguration und eine Entität, die existiert, um zu wachsen.

Entscheidungen, die einen genaueren Blick verdienen

Warum ResultRecord eingefroren ist. Ein abgeschlossenes Ergebnis ist eine Tatsache über etwas bereits Geschehenes. Nichts sollte den Punktestand von letzter Woche bearbeiten können. Einfrieren drückt das auf eine Weise aus, die ein Kommentar nicht leisten kann.

Warum Gradebook es nicht ist. Es soll Ergebnisse sammeln. Einfrieren würde bedeuten, das gesamte Notenbuch für jedes Ergebnis neu aufzubauen. eq=False erhält außerdem die Identität der Entität: Zwei getrennte Notenbücher werden nicht allein deshalb gleich, weil ihre derzeitigen Datensätze übereinstimmen.

Warum Gradebook eine pro Objekt erzeugte Liste hält. Lektion 4 und Lektion 8 zeigten denselben Fehler auf zwei Wegen: einen veränderbaren Standardwert und ein veränderbares Klassenattribut. Ein Gradebook pro Programm ist der übliche Fall. Genau deshalb würde eine zwischen zwei Notenbüchern geteilte Liste lange unbemerkt bleiben.

Warum ist ResultRecord eingefroren, Gradebook aber nicht?

Die Methoden, die keine Boilerplate sind

@dataclass schreibt Konstruktoren, repr-Darstellungen und Vergleiche. Was übrig bleibt, ist deine Aufgabe, und damit verbringt die Bewertung die meiste Zeit:

  • Question.from_dict und Question.simple, zwei weitere Zugänge;

  • Question.normalize, die Hilfsfunktion, die zu Fragen gehört;

  • Question.is_correct und points_for, die Regeln einer Frage;

  • ResultRecord.percentage und passed_under, Aussagen über ein Ergebnis;

  • Gradebook.record, best und average, die normierte Anteile vergleichen, damit Ergebnisse mit unterschiedlichen Gesamtpunktzahlen aussagekräftig bleiben.

Keine dieser Methoden hätte automatisch erzeugt werden können, denn jede drückt eine getroffene Entscheidung aus.

Fertigstellen

Zähle die Zeilen, die du nicht schreiben musstest. Lies dann die selbst geschriebenen und prüfe, ob jede etwas aussagt, das sich nicht bereits aus der Attributliste darüber ergeben hätte.

Ergänze zwei Ergebnisse mit unterschiedlichen Gesamtpunktzahlen, etwa 9/100 und 8/10, und gib das beste Ergebnis sowie den durchschnittlichen Anteil aus. Dieser letzte Lauf zeigt, dass das Notenbuch Ergebnisse modelliert und nicht bloß die vorgegebenen Beispiele besteht.

Aufgabe

Baue die fünf Klassen und verwende die erzeugte Mechanik für alles, was sie abdeckt.

Points ist ein eingefrorener Wert mit einem positiven amount. plus(other) gibt ein neues Points zurück.

Question ist ein eingefrorener, validierter Wert mit nicht leeren prompt und answer sowie points mit dem Standard Points(1).

QuizConfig ist eine veränderbare Dataclass: ein erforderlicher nicht leerer title, pass_mark mit dem Standard 0.6 und einem erlaubten Bereich über 0 bis einschließlich 1 sowie tags, das für jede Konfiguration als neue leere Liste beginnt.

ResultRecord ist ein eingefrorener Wert mit einem nicht leeren learner, einem nicht negativen total und einem score von 0 bis einschließlich total. Er bietet percentage und eine Methode passed_under(config).

Gradebook ist eine veränderbare Entität mit einem nicht leeren title und einer internen Liste _records. Verwende eq=False. Es erhält record(result), records() für eine sichere Kopie, best() und average().

Vervollständige die im Startcode genannten Methoden:

  • Question.from_dict(record) liest prompt, answer und einen Points-Wert aus points; simple(prompt, answer) verwendet Points(1). Beide sind Klassenmethoden und erzeugen über cls. Lehne ein Punkteargument, das kein Points ist, mit TypeError ab.

  • Question.normalize(text) ist eine statische Methode, die Leerraum an den Rändern entfernt und den Text in Kleinbuchstaben umwandelt. Verwende sie in is_correct auf beiden Seiten. points_for gibt bei einer richtigen Antwort den ganzzahligen Betrag zurück, andernfalls 0.

  • QuizConfig.practice(title) und final_exam(title) sind Klassenmethoden, die Bestehensgrenzen von 0.5 beziehungsweise 0.8 wählen. Die Tags beginnen weiterhin leer.

  • ResultRecord.percentage ist der Anteil score / total oder 0.0 bei einer Gesamtsumme von null. passed_under(config) schließt ein Ergebnis exakt auf der Bestehensgrenze der Konfiguration ein.

  • Gradebook.record akzeptiert nur ResultRecord-Werte und löst andernfalls TypeError aus. best() vergleicht Anteile und gibt ein höchstes Ergebnis zurück oder None, wenn keines vorhanden ist. average() ist der Mittelwert der erfassten Anteile oder 0.0, wenn keine vorhanden sind.

Verwende für die oben beschriebenen ungültigen Feldwerte ValueError. Halte den Verlauf des Notenbuchs mit init=False, repr=False aus den Konstruktorargumenten und der erzeugten repr-Darstellung heraus, wie in Lektion 7. Keine Klasse verwendet eine Ausgabe.