Dataclasses und Wertobjekte · Übung
Veränderbare Standardwerte sicher erzeugen
Eine Zahl lässt sich gefahrlos als Standardwert teilen, weil sie unveränderlich ist. Eine Liste ist anders: Jedes Objekt braucht seine eigene.
Dataclasses lehnen die verlockende Version ab:
from dataclasses import dataclass
@dataclass
class QuizAttempt:
learner: str
responses: list = []
Würde bei der Klassendefinition eine Liste erzeugt, erhielte jeder Versuch dasselbe Objekt.
Jedes Mal eine neue Liste anfordern
Verwende field(default_factory=list):
eq=False schaltet die erzeugte Wertgleichheit aus. Ein QuizAttempt repräsentiert einen Versuch. Zwei getrennte Versuche sollen deshalb ungleich bleiben, auch wenn Lernende und Antworten übereinstimmen. So bleibt die Entitätsidentität aus Kapitel 7 erhalten.
Die Fabrik ist die Funktion list, kein Aufruf davon. Der erzeugte Konstruktor ruft diese Funktion jedes Mal auf, wenn er einen Standardwert braucht. Dadurch entsteht eine neue Liste für die neue Instanz.
Warum lautet die Schreibweise default_factory=list statt default_factory=list()?
Der Fehler kann verborgen bleiben
Gemeinsame veränderbare Standardwerte sind besonders gefährlich, wenn das normale Programm nur ein Objekt erzeugt. Das zweite Objekt entsteht vielleicht erst Monate später, nachdem das erste bereits Daten angesammelt hat.
Verwende eine Fabrik für veränderbaren Anfangszustand, den jedes Objekt selbst besitzen soll:
| Anfangswert | Felddefinition |
|---|---|
| Neue leere Liste | field(default_factory=list) |
| Neues leeres Dictionary | field(default_factory=dict) |
| Zahl oder String | Ein direkter Standardwert |
Die Übung gibt sowohl einer Konfiguration als auch einem Versuch eigene Listen. Die Bewertung erzeugt Paare und verändert jeweils nur eines der beiden Objekte.
Aufgabe
Gib jedem Objekt einen eigenen veränderbaren Standardwert.
QuizConfig hat einen erforderlichen title und tags, das für jede Konfiguration als neue leere Liste beginnt.
QuizAttempt hat einen erforderlichen learner und responses, das für jeden Versuch als neue leere Liste beginnt. Verwende eq=False, weil Versuche Entitäten mit eigener Identität sind.
Verwende für beide Listen field(default_factory=list). Die Bewertung verändert ein Objekt und prüft, ob die Liste des anderen leer bleibt.