0%

Samenwerkende objecten · oefening

Bewaar een collectie objecten

Eén Question is nog geen quiz. Een quiz bestaat uit meerdere vragen, in een volgorde die iemand heeft gekozen, met een titel erboven.

Je weet al hoe je meerdere dingen bewaart: in een lijst. Nieuw is dat de lijst bij een object hoort en dat dat object bepaalt wat ermee mag gebeuren.

Een lijst binnen een klasse

Try it

_questions is intern, om de redenen uit hoofdstuk 5. De buitenwereld krijgt in plaats daarvan add, een aantal en een totaal. Elk daarvan gaat over de quiz, niet over de lijst.

Kijk naar total_points. Die loopt over Question-objecten en vraagt elk object naar de eigen punten. De Quiz komt nooit te weten hoe een vraag bepaalt hoeveel die waard is en zou blijven werken als dat morgen een berekende property werd.

Een collectie objecten doorlopen

Een lus over objecten ziet er precies zo uit als een lus over getallen. Het verschil zit in wat je binnen de lus kunt doen:

Try it

Dat werkte, en het ging rechtstreeks langs de klasse naar _questions. In echte code is die regel een waarschuwingssignaal. Als de buitenwereld de vragen moet doorlopen, hoort de quiz een manier te bieden die er niet van afhangt dat de collectie een lijst is:

Try it

Hoofdstuk 11 biedt een veel betere manier om dit te schrijven, door de quiz zelf iets te maken dat een for-lus begrijpt. Voor nu is een methode die de vragen teruggeeft duidelijk en voldoende.

Een kopie teruggeven, en waarom

questions() geeft list(self._questions) terug, niet self._questions. Die ene aanroep doet ertoe:

Try it

De aanroeper verwijderde een item uit de ontvangen lijst en de quiz meldt nog steeds beide vragen. Geef je de interne lijst rechtstreeks terug, dan zou dezelfde pop() ongemerkt een vraag uit de quiz hebben weggegooid, via een variabele die op een eigen kopie leek.

Dit is het gedrag van veranderbare lijsten uit Python-basis I, nu op een plek waar het echte schade veroorzaakt. Een methode die de eigen lijst van het object uitdeelt, maakt alles ongedaan waarvoor de underscore bedoeld was.

Waarom geeft questions() list(self._questions) terug in plaats van self._questions?

Let op wat list() wel en niet beschermt. De nieuwe lijst is afzonderlijk, dus toevoegen en verwijderen raken de quiz niet. De Question-objecten erin zijn dezelfde objecten, dus een aanroeper kan er nog steeds één veranderen. Hoofdstuk 8 komt daarop terug met waarden die helemaal niet kunnen worden veranderd.

De oefening

Je bouwt de Quiz die de rest van het hoofdstuk gebruikt. Die bewaart vragen, rapporteert erover en geeft de lijst die bepaalt welke vragen erbij horen niet weg. De kopie bevat nog steeds dezelfde Question-objecten, precies zoals de uitleg over oppervlakkig kopiëren hierboven beschrijft.

Opdracht

Maak de collectiegrens van de gegeven klasse Quiz af.

add(question) voegt een vraag toe en weigert None met een ValueError. questions() geeft de vragen op volgorde terug in een nieuwe lijst, zodat toevoegen aan of verwijderen uit de teruggegeven lijst niet kan veranderen welke vragen bij de quiz horen.

question_at, question_count, total_points en hardest zijn gegeven, zodat je je kunt richten op de ene nieuwe moeilijkheid: een collectie veilig beheren.

Voer het programma uit en controleer of de laatste regel 3 is: een item verwijderen uit de lijst die questions() teruggeeft moet alle drie de vragen in de quiz laten staan.