Dataclasses en waardeobjecten · oefening
Maak veranderbare standaardwaarden veilig
Een getal kan veilig als standaardwaarde worden gedeeld omdat het niet kan worden veranderd. Bij een lijst is dat anders: elk object heeft een eigen lijst nodig.
Dataclasses weigeren de verleidelijke versie:
from dataclasses import dataclass
@dataclass
class QuizAttempt:
learner: str
responses: list = []
Als er één lijst werd gemaakt bij het definiëren van de klasse, zou elke poging hetzelfde object ontvangen.
Vraag elke keer om een nieuwe lijst
Gebruik field(default_factory=list):
eq=False schakelt gegenereerde waardegelijkheid uit. Een QuizAttempt stelt één poging voor, dus twee afzonderlijke pogingen moeten ongelijk blijven, ook als hun cursist en antwoorden overeenkomen. Dit behoudt de entiteitsidentiteit uit hoofdstuk 7.
De factory is de functie list, geen aanroep ervan. De gegenereerde constructor roept die functie telkens aan als er een standaardwaarde nodig is en maakt zo een nieuwe lijst voor de nieuwe instantie.
Waarom schrijf je dit als default_factory=list in plaats van default_factory=list()?
De fout kan verborgen blijven
Gedeelde veranderbare standaardwaarden zijn vooral gevaarlijk als het gewone programma maar één object maakt. Het tweede object komt misschien pas maanden later, nadat het eerste gegevens heeft verzameld.
Gebruik een factory voor veranderbare begintoestand per object:
| Beginwaarde | Velddefinitie |
|---|---|
| Nieuwe lege lijst | field(default_factory=list) |
| Nieuwe lege dictionary | field(default_factory=dict) |
| Getal of string | Een directe standaardwaarde |
De oefening geeft zowel een configuratie als een poging een eigen lijst. De beoordelaar maakt paren en verandert slechts één object van elk paar.
Opdracht
Geef elk object een eigen veranderbare standaardwaarde.
QuizConfig heeft een verplichte title en tags die per configuratie als een nieuwe lege lijst begint.
QuizAttempt heeft een verplichte learner en responses die per poging als een nieuwe lege lijst begint. Gebruik eq=False, omdat pogingen entiteiten zijn die op identiteit zijn gebaseerd.
Gebruik field(default_factory=list) voor beide lijsten. De beoordelaar verandert één object en controleert of het andere leeg blijft.