Zusammenarbeit zwischen Objekten · Übung
Eine Sammlung von Objekten verwalten
Ein einzelnes Question-Objekt ist noch kein Quiz. Ein Quiz besteht aus mehreren Fragen in einer gewählten Reihenfolge und einem Titel darüber.
Du weißt bereits, wie du mehrere Dinge speicherst: in einer Liste. Neu ist, dass diese Liste zu einem Objekt gehört und das Objekt entscheidet, was damit geschehen darf.
Eine Liste innerhalb einer Klasse
_questions ist aus den in Kapitel 5 besprochenen Gründen intern. Nach außen gibt es stattdessen add, eine Anzahl und eine Summe. All das beschreibt das Quiz, nicht die Liste.
Betrachte total_points. Es durchläuft Question-Objekte und fragt jedes nach seinen Punkten. Das Quiz erfährt nie, wie eine Frage ihren Wert bestimmt. Es würde weiter funktionieren, wenn daraus morgen eine berechnete Property würde.
Eine Sammlung von Objekten durchlaufen
Eine Schleife über Objekte sieht genauso aus wie eine Schleife über Zahlen. Der Unterschied liegt darin, was du in ihr tun kannst:
Das hat funktioniert und direkt an der Klasse vorbei auf _questions zugegriffen. In echtem Code ist diese Zeile ein Warnsignal. Muss äußerer Code die Fragen durchlaufen, sollte das Quiz einen Weg anbieten, der nicht davon abhängt, dass die Sammlung eine Liste ist:
Kapitel 11 zeigt einen deutlich besseren Weg: Das Quiz selbst wird zu etwas, das eine for-Schleife versteht. Vorerst ist eine Methode, die die Fragen zurückgibt, ein klarer und ausreichender Weg.
Eine Kopie zurückgeben und warum
questions() gibt list(self._questions) zurück, nicht self._questions. Dieser eine Aufruf ist wichtig:
Der aufrufende Code hat ein Element aus der erhaltenen Liste entfernt, und das Quiz meldet weiterhin beide Fragen. Würdest du die interne Liste direkt zurückgeben, hätte derselbe Aufruf von pop() unbemerkt eine Quizfrage entfernt, über eine Variable, die wie eine eigene Kopie aussah.
Hier begegnet dir das Verhalten veränderbarer Listen aus Grundlagen I wieder, nun an einer Stelle, wo es echten Schaden anrichtet. Eine Methode, die die eigene Liste des Objekts herausgibt, macht alles zunichte, wofür der Unterstrich gedacht war.
Warum gibt questions() die Liste mit list(self._questions) statt mit self._questions zurück?
Beachte, was list() schützt und was nicht. Die neue Liste ist eigenständig, Hinzufügen und Entfernen erreichen das Quiz daher nicht. Die darin enthaltenen Question-Objekte sind dieselben Objekte. Aufrufender Code könnte also weiterhin eines von ihnen ändern. Kapitel 8 greift das mit Werten auf, die sich überhaupt nicht ändern lassen.
Die Übung
Du baust das Quiz, das der Rest des Kapitels verwendet. Es hält Fragen, liefert Informationen über sie und gibt die Liste, die über seine enthaltenen Fragen bestimmt, nicht heraus. Die Kopie enthält weiterhin dieselben Question-Objekte, genau wie oben bei der flachen Kopie beschrieben.
Aufgabe
Vervollständige die Schnittstelle zur Sammlung in der vorgegebenen Klasse Quiz.
add(question) hängt eine Frage an und lehnt None mit ValueError ab. questions() gibt die Fragen in ihrer Reihenfolge in einer neuen Liste zurück. Hinzufügen oder Entfernen in dieser zurückgegebenen Liste darf die Fragen des Quiz nicht verändern.
question_at, question_count, total_points und hardest sind vorgegeben, damit du dich auf die eine neue Schwierigkeit konzentrieren kannst: eine Sammlung sicher zu verwalten.
Führe das Programm aus und bestätige, dass die letzte Zeile 3 lautet: Wenn ein Element aus der von questions() zurückgegebenen Liste entfernt wird, muss das Quiz weiterhin alle drei Fragen enthalten.