Inventoryt som glömmer
Tre varv, ett föremål
Tänk dig ett textäventyr där spelaren plockar upp föremål. Du skriver en loop, skapar en lista och lägger till föremål med .append(). Det ser rimligt ut. Men efter tre varv innehåller listan ett enda föremål, och det är det senaste. Allt annat är borta.
Redo.
Kör koden och läs de tre utskrifterna. Varje varv skriver ut en lista med exakt ett föremål. Fundera ett ögonblick på vilken rad som är boven, innan du läser vidare.
Boven är inventory = []. Ett likhetstecken är en tilldelning, precis som i Lådor i RAM & datatyper: raden säger “gör en ny, tom lista och låt inventory vara namnet på den”. Den raden ligger inuti loopen, alltså körs den om vid varje varv, och varje varv börjar med en helt ny lista. Föremålet från förra varvet försvann inte för att .append() gjorde fel, utan för att listan det låg i byttes ut.
Flytta raden inventory = [] så att den står ovanför for-raden, utan indrag, och kör igen. Nu växer listan varv för varv.
Regeln du redan har använt
Det här är samma princip som i Listan & loopen, där räknaren måste sättas till noll före loopen och inte inuti den. Nu vet du varför: en tilldelning inuti loopen börjar om från början varje varv, oavsett om det är en nolla eller en tom lista.
Mönstren på Mönsterkortet vilar alla på den regeln. Räknaren och Samlaren startar utanför loopen och uppdateras inuti den. Samlingsbyggaren skapar sin lista utanför och fyller den inuti. Ser du ett mönster som börjar med “skapa en tom” och fortsätter med “för varje”, så hör de två raderna till olika ställen i koden.
Två namn, samma lista
Det finns en följd av det här som överraskar de flesta första gången:
Redo.
Kör koden. Du lade till fyran via b, men den syns i båda utskrifterna. b = a skapade ingen ny lista, den gav den befintliga listan ett andra namn. Variabeln håller inte själva listan, den håller reda på vilken lista det är, och nu håller båda reda på samma.
Ändra raden till b = a[:] och kör igen. Nu är a orörd, för hakparenteserna utan innehåll betyder “en kopia av hela listan”, och då finns det två listor att hålla reda på.
Det här är sällan ett problem, men när det är ett problem är det svårt att se: en lista du trodde var din egen ändras av kod någon annanstans i programmet.
Append på ingenting
Det finns en variant av inventory-felet som ger ett tydligare, men förvirrande, felmeddelande:
Redo.
None är inte en lista. Det är ingenting, och ingenting har inga metoder, varken .append() eller .remove(). AttributeError Uppstår när du försöker anropa en metod eller komma åt en egenskap som inte finns på ett objekt. 'NoneType' object has no attribute 'append' innebär att variabeln är None, inte en lista. med NoneType i meddelandet betyder alltid samma sak: variabeln innehåller None, men du behandlar den som ett objekt med funktionalitet.
Uppgift: Ett inventory som minns
Skapa filen inventory.py.
Skapa en tom lista
inventory = []utanför alla loopar.Bygg en
while True-loop med en meny: lägg till föremål, visa inventoryt, ta bort ett föremål eller avsluta.Implementera “ta bort” med
.remove(). Hantera fallet där föremålet inte finns, kontrollera medif föremål in inventory:innan du anropar.remove()och skriv ut ett tydligt meddelande om det saknas.Kraschsäkra menyvalet. Du har två verktyg för det nu: Valideraren från Kraschsäkring, och
läs_int()från Ditt eget bibliotek som lämnar tillbaka ett garanterat heltal. Använd den, så överlever menyn att någon skriver en bokstav.Gå till Source Control-panelen i VS Code, stagea filen, skriv ett meningsfullt commit-meddelande och pusha.
Vad händer om
Du flyttar in raden inventory = [] i loopen och kör programmet. Beskriv exakt vad som händer när du lägger till tre föremål efter varandra, och varför.
Motivera & reflektera
Du skriver extra = inventory och lägger till ett föremål via extra. Vad innehåller inventory efteråt, och vad hade du behövt skriva i stället för att lämna originalet orört? Motivera varför skillnaden spelar roll i ett program där flera funktioner arbetar med samma lista.