Voeg een open taak toe
Laat add_task een nieuwe taak met done=False toevoegen en None teruggeven.
Een takenoverzicht moet soms veranderen en soms alleen een overzicht geven. Als het tonen van open taken per ongeluk ook je oorspronkelijke lijst verandert, wordt het programma moeilijk te vertrouwen. Duidelijke afspraken over lezen, wijzigen en kopiëren voorkomen zulke verrassingen.
Een taak is in deze oefening een dictionary met twee velden: title bevat tekst en done een boolean. De buitenste lijst bepaalt de volgorde van de taken. Je combineert daarmee precies de collecties die je in de vorige lessen hebt leren gebruiken.
De mutatieafspraak van een functie vertelt of zij bestaande gegevens mag veranderen. Een functie die een taak toevoegt of afrondt mag dat hier wel. Een functie die alleen open titels opzoekt mag de bron juist niet aanpassen.
Kies die afspraak voordat je gaat programmeren. Kijk daarna apart naar de retourwaarde: een wijziging kan None teruggeven, terwijl een leesfunctie een nieuwe lijst oplevert. Het type van het antwoord zegt op zichzelf nog niet of de bron is gewijzigd.
Het zelfstandige voorbeeld gebruikt geplande bezoeken. Een bezoek bevestigen verandert bewust het bestaande record; het maken van een overzicht levert juist losse recordkopieën op. Druk op Voorbeeld uitvoeren en vergelijk de oorspronkelijke plaatsnaam met de aangepaste naam in het overzicht.
def confirm_visit(visits, index):
visits[index]["confirmed"] = True
visits = [
{"place": "museum", "confirmed": False},
{"place": "park", "confirmed": True},
]
confirm_visit(visits, 0)
print(visits[0]["confirmed"])
confirm_visit(visits, 0)
print(visits[0]["confirmed"])
summary = [visit.copy() for visit in visits if visit["confirmed"]]
summary[0]["place"] = "tuin"
print(visits[0]["place"])
print(summary[0]["place"])
print(len(summary))True True museum tuin 2
De bevestigingsfunctie wordt tweemaal voor hetzelfde bezoek aangeroepen. Na beide aanroepen blijft confirmed True. Dit is dezelfde eigenschap die je bij het toevoegen aan een set zag: herhalen hoeft geen nieuwe verandering te veroorzaken.
Bij het overzicht maakt de comprehension een nieuwe buitenste lijst. copy op iedere dictionary maakt bovendien een nieuwe dictionary met dezelfde velden. Daardoor schrijft de aangepaste plaatsnaam niet terug naar het oorspronkelijke bezoek.
visits ──► lijst A ──► record A: plaats museum
summary ──► lijst B ──► record B: plaats tuinDe veldwaarden zijn hier tekst en booleans. Die veranderen niet van binnenuit, zodat een oppervlakkige dictionarykopie voldoende is. Bij een veld dat zelf een veranderbare lijst bevat, zou je opnieuw moeten bepalen of ook die lijst een kopie nodig heeft.
Een filter maakt wel een nieuwe lijst, maar kopieert haar elementen niet vanzelf. Deze zelfstandige foutvariant verzamelt de oorspronkelijke records in een nieuw overzicht. Daarna krijgt één record via dat overzicht een andere naam.
visits = [{"place": "museum", "confirmed": True}]
summary = [visit for visit in visits if visit["confirmed"]]
summary[0]["place"] = "tuin"
print(visits[0]["place"])
print(summary is visits)
print(summary[0] is visits[0])tuin False True
Ook de plaatsnaam in de bron wordt tuin. De nieuwe buitenste lijst was dus echt nieuw, maar wees nog naar dezelfde dictionary. Het probleem is hetzelfde kopieerniveau als bij gedeelde rijen, nu met dictionaries als binnenste objecten.
Kopieer daarom ieder behouden record wanneer wijzigingen in het resultaat niet mogen terugwerken. Een andere variabelenaam of een nieuwe buitenste lijst is daarvoor onvoldoende. Controleer bij een test ook wat er na een wijziging aan het resultaat met de bron gebeurt.
De vier opdrachten hebben bewust verschillende effecten. Een nieuwe taak begint open, dus met done=False. De titel komt uit de invoer; gelijke titels mogen voorkomen en deze oefening gebruikt nog geen taak-ID's.
| Functie | Mag de bron veranderen? | Antwoord |
|---|---|---|
| add_task | Ja: achteraan een nieuw taakrecord | None |
| finish_task | Ja: de gekozen taak wordt voltooid | None |
| open_titles | Nee | Nieuwe lijst met open titels |
| prune_done | Nee | Nieuwe lijst met kopieën van open taakrecords |
De index voor afronden is geldig. Zet de toestand op True; wissel haar niet om met not, want dan zou tweemaal afronden de taak weer openen. Alleen done bepaalt of een taak klaar is, niet haar titel.
Bij beide leesfuncties blijft de bestaande volgorde behouden. Een lege bron of een bron met uitsluitend voltooide taken levert een lege lijst op. Ook dan moet de oorspronkelijke bron intact blijven.
Laat add_task een nieuwe taak met done=False toevoegen en None teruggeven.
Laat finish_task dezelfde taak blijvend op done=True zetten.
Laat open_titles titels van niet-voltooide taken in dezelfde volgorde teruggeven.
Laat prune_done alleen open taken kopiëren, zonder de oorspronkelijke lijst of dictionaries te wijzigen.
Je kunt nu gegevens combineren en bewust veranderen. Deze transferles oefent het model; later bouw je een zelfstandig project met meerdere bestanden en mijlpalen, en de volgende inhoudsmodule verdiept tekstbewerking.
Voer je code uit en ontdek
wat je hebt gemaakt.
Je hoeft het niet in één keer goed te doen. Na het uitvoeren zie je hier welke stappen al kloppen.