3.1 Lister og listeoperasjoner
Lister, indeksering, slicing og de vanligste metodene — og snarveiene max/min/sum/sorted som fasiten bruker fritt.
Lista er datastrukturen alt annet i faget står på, og den er med i alle de ti eksamenssittingene i arkivet (2014–2019) — 100 %. Uten lister finnes verken 2D-tabellen som filene leses inn i, eller analysefunksjonene som teller og rangerer.
Slik dukker kapitlet opp på settet:
- Sjanger B — kodesporing, altså «hva skriver dette programmet ut?». Du får en kort snutt og skal skrive utskriften tegn for tegn. Utsnitt med steg (tall[::-1], tall[1::2]) er en fast innpakning her, og gir 1–3 % per deloppgave.
- Sjanger G — analyse, altså «finn den største / tell hvor mange / rangér». Dette er tyngdepunktet i programmeringsoppgaven, 5–10 % per deloppgave, og starter alltid med en liste.
Snarveiene er lov. I dette emnet bruker de offisielle løsningsforslagene fritt max, min, sum, sorted og .index. Du taper ingenting på å bruke dem — men du må også kunne skrive løkka som gjør det samme, fordi noen oppgaver spør etter mer enn maksverdien. Kapitlet viser derfor begge, side om side.
Prioritet: dette må sitte (høyeste av bokas tre nivåer «må sitte», «må kunne», «bør kjenne til»). Bruk 40 minutter her, og du har fjernet en hel klasse feil senere i boka.
- kap. 1.3 — løkker: for, while og range. Du bør kjenne igjen at range(start, stopp, steg) stopper FØR stopp.
- kap. 1.1 — datatyper og konvertering med int() og float().
Vil du ha den samme grunnmuren i en annen innpakning først, dekker Lister det samme stoffet i et parallelt emne.
Har du aldri programmert før, er det bare to ord du trenger herfra: en variabel er et navn som peker på en verdi, og en løkke er en kodeblokk som gjentas én gang for hvert element. Alt annet forklares her.
Løkke 1 — lista som beholder (~7 min)
Rutetabellen for bybussen din har fire avganger på morgenen. Du kunne laget fire variabler — avgang1, avgang2, avgang3, avgang4 — men da må koden skrives om hver gang det kommer en avgang til, og du kan ikke gå gjennom dem med en løkke.
En liste løser dette: ett navn som peker på flere verdier i en bestemt rekkefølge. Du skriver verdiene i hakeparenteser med komma mellom.
En liste er en ordnet samling verdier under ett navn. Rekkefølgen er en del av dataene, og den samme verdien kan stå flere ganger. Verdiene kan være av forskjellig type i samme liste, men i praksis holder du dem like.
Du skriver den som verdier i hakeparenteser, atskilt med komma: avganger = [612, 627, 642, 657]. En tom liste er [], og den er ofte startpunktet: du oppretter den tom og fyller på underveis.
Gir antall elementer i lista x, som et heltall. En tom liste har lengde 0.
len er den vanligste kilden til løkkegrenser: range(len(x)) gir nøyaktig plassnumrene — indeksene — 0, 1, … opp til len(x) - 1, altså alle lovlige plasser i lista og ingen flere. Indeksering gjennomgås i løkke 2 rett under.
Legg de fire morgenavgangene 06:12, 06:27, 06:42 og 06:57 i en liste med minutter etter midnatt, skriv ut hele lista og hvor mange avganger det er.
Klokkeslettet 06:12 er 6 * 60 + 12 = 372 minutter etter midnatt. De fire tallene legges rett inn i lista.
avganger = [372, 387, 402, 417]
print(avganger)
print(len(avganger))Utskrift:
[372, 387, 402, 417]
4Legg merke til hvordan Python skriver ut ei liste: med hakeparenteser og komma-mellomrom mellom elementene. På en kodesporingsoppgave må du gjengi akkurat den formen — [372, 387, 402, 417], ikke 372 387 402 417.
(Innstegsoppgave, sjanger B — kodesporing.) Hva skriver programmet ut? Skriv begge linjene nøyaktig slik terminalen viser dem.
priser.append(18) legger verdien 18 bakerst i lista.
priser = [39, 25, 61]
priser.append(18)
print(priser)
print(len(priser))Løkke 2 — indeksering: å plukke ut ett element (~8 min)
Hvert element i lista har et indeksnummer, altså plassnummeret sitt. Tellingen starter på 0, ikke på 1. Første avgang er avganger[0], andre er avganger[1], og siste i en firelisteliste er avganger[3].
Det er derfor range(len(x)) passer så godt: den gir nettopp 0 til og med len(x) - 1.
Henter ut elementet på plass i i lista x. Tellingen begynner på 0, så x[0] er det første elementet og x[len(x) - 1] er det siste.
Du kan også tilordne: x[2] = 99 bytter ut elementet på plass 2. Er i større enn eller lik len(x), stopper programmet med feilen IndexError: list index out of range.
Teller bakfra: x[-1] er siste element, x[-2] det nest siste. Det finnes ingen x[-0] — null er null, så x[0] er alltid det første.
Nytten er at du slipper x[len(x) - 1] for å nå slutten. x[-1] er kortere og brukes fritt i løsningsforslag.
Med avganger = [372, 387, 402, 417]: skriv ut første og siste avgang, flytt den tredje avgangen fem minutter senere, og skriv ut lista igjen.
Den tredje avgangen har indeks 2, fordi tellingen starter på 0.
avganger = [372, 387, 402, 417]
print(avganger[0], avganger[-1])
avganger[2] = avganger[2] + 5
print(avganger)Utskrift:
372 417
[372, 387, 407, 417]print med to argumenter setter ett mellomrom mellom dem — derfor 372 417 og ikke 372417. Den detaljen er verdt poeng på utskriftssporing.
- Å tro at x[1] er det første elementet. Det er det andre.
- Å skrive x[len(x)] for å nå det siste. Det er én for langt og gir IndexError; siste element er x[len(x) - 1] eller x[-1].
- Å bruke range(1, len(x)) når du vil ha alle elementene. Da hopper du over det første.
Motgiften er mekanisk: skriv indeksene 0, 1, 2, 3 over elementene på kladdearket før du svarer.
Hva skriver programmet ut?
maling = [4, 9, 2, 7, 5]
maling[0] = maling[-1]
maling[-2] = maling[1]
print(maling)Løkke 3 — utsnitt (slicing) (~10 min)
Ofte vil du ikke ha ett element, men en bit av lista: de tre første avgangene, annenhver måling, eller hele lista baklengs. Det kalles et utsnitt (på engelsk slice), og skrives med kolon inne i hakeparentesene.
Dette er den enkeltkonstruksjonen som oftest dukker opp i kodesporing på dette emnet, så den skal sitte helt presist.
Gir en ny liste med elementene fra og med indeks a til men ikke med indeks b. Sluttgrensa er altså eksklusiv, akkurat som i range.
Utelater du a, starter utsnittet på 0; utelater du b, går det til enden. x[:2] er de to første, x[2:] er resten, og x[:] er en kopi av hele lista. Originalen endres aldri av et utsnitt.
Samme som x[a:b], men hopper s plasser om gangen. x[::2] gir elementene på indeks 0, 2, 4 …, og x[1::2] gir dem på indeks 1, 3, 5 … — altså «annethvert element», to forskjellige varianter av det.
Hvilken av de to oppgaven mener, avgjøres av om den teller fra det første eller det andre elementet. Les oppgaveteksten to ganger her.
Negativt steg går bakover. x[::-1] gir en ny liste med elementene i motsatt rekkefølge, og lar originalen stå urørt.
Det samme trikset virker på tekst: 'stein'[::-1] gir 'niets'. Skal du derimot snu lista på plass, uten å lage en ny, bruker du x.reverse().
La tall = [8, 3, 5, 1, 9, 4]. Skriv ut de tre første, alt fra og med indeks 3, annethvert element fra indeks 1, og hele lista baklengs — og vis at originalen er uendret etterpå.
Fire utsnitt, én utskrift hver, og lista til slutt.
tall = [8, 3, 5, 1, 9, 4]
print(tall[:3])
print(tall[3:])
print(tall[1::2])
print(tall[::-1])
print(tall)Utskrift:
[8, 3, 5]
[1, 9, 4]
[3, 1, 4]
[4, 9, 1, 5, 3, 8]
[8, 3, 5, 1, 9, 4]Gå gjennom dem én for én: tall[:3] stopper før indeks 3, så 1-tallet er ikke med. tall[3:] begynner nettopp på indeks 3, så det er 1-tallet som er første element der. tall[1::2] tar indeks 1, 3 og 5. Og siste linje viser poenget: originalen er den samme som før — et utsnitt lager alltid en ny liste.
Hva skriver programmet ut? Skriv alle fire linjene nøyaktig, med hakeparenteser og komma-mellomrom.
m = [10, 20, 30, 40, 50, 60, 70]
print(m[2:5])
print(m[::3])
print(m[-3:])
print(len(m[1:4]))x[2] gir elementet på plass 2. x[2:3] gir ei liste med det ene elementet. De ser like ut, men x[2] + 1 virker mens x[2:3] + 1 stopper programmet — du kan ikke legge et tall til ei liste.Den andre halvparten av fellen er utskriften: et element skrives 30, ei liste skrives [30]. På kodesporing er de to forskjellige svar, og bare det ene gir poeng.
Løkke 4 — metodene som endrer lista (~10 min)
En metode er en funksjon som hører til et objekt, og som kalles med punktum: avganger.append(432). De fleste listemetodene endrer lista du kaller dem på, og returnerer ingenting.
Definisjonene under er flashcard-stoff — du kan trygt bla forbi dem ved førstegangslesing og komme tilbake når du pugger. Det du må ta med deg nå, er skillet mellom «endrer lista» og «gir en ny».
Legger verdien v bakerst i lista x. Lista blir ett element lengre, og metoden returnerer None — du skriver aldri x = x.append(v).
Dette er arbeidshesten i hele faget: du oppretter resultat = [] og fyller på med append inne i en løkke.
Setter v inn PÅ plass i, og skyver resten ett hakk bakover. x.insert(0, v) legger altså verdien først.
Er i større enn lengden, havner verdien bakerst i stedet for å gi feil. Lista endres på plass, og metoden returnerer None.
Fjerner elementet på plass i OG returnerer det. Uten argument, x.pop(), fjernes og returneres det siste elementet.
Dette er den ene fjerne-metoden som gir deg verdien tilbake, så den brukes når du både vil ta ut og bruke et element.
Fjerner den FØRSTE forekomsten av verdien v — ikke alle. Metoden tar en verdi, ikke en indeks, og returnerer None.
Finnes ikke verdien i lista, stopper programmet med ValueError. Sjekk derfor med if v in x: først når du er usikker.
Gir indeksen til den første forekomsten av v. Finnes ikke verdien, stopper programmet med ValueError.
Dette er konstruksjonen som senere lar deg finne en kolonne ved navn i stedet for å hardkode et kolonnenummer — et eksplisitt krav for full uttelling på eksamen.
Gir antall ganger verdien v forekommer i lista, som et heltall. Er verdien ikke der, gir den 0 — den stopper altså aldri programmet.
x.count(v) er den korteste måten å telle på når du bare skal telle ÉN verdi. Skal du telle alle verdiene, trenger du en ordbok.
Sorterer lista stigende PÅ PLASS og returnerer None. Lista du kalte den på er endret etterpå; den gamle rekkefølgen er borte.
x.sort(reverse=True) sorterer synkende. Vil du ha en sortert kopi og beholde originalen, bruker du sorted(x) i stedet.
Snur rekkefølgen i lista på plass og returnerer None. Ingen ny liste lages.
Forskjellen fra x[::-1] er nettopp der: utsnittet gir en ny, snudd liste og lar originalen stå, mens reverse() endrer originalen.
Rutetabellen [372, 387, 402, 417] skal endres: en ny avgang 06:02 settes inn først, avgangen 06:42 utgår, og den siste avgangen flyttes til en egen variabel og tas ut av lista. Skriv ut lista og den uttatte avgangen.
06:02 er 6 * 60 + 2 = 362 minutter, og 06:42 er 402. Merk at pop er den eneste av de tre som gir en verdi tilbake.
avganger = [372, 387, 402, 417]
avganger.insert(0, 362)
avganger.remove(402)
siste = avganger.pop()
print(avganger)
print(siste)Utskrift:
[362, 372, 387]
417remove(402) tar verdien 402, ikke plass nummer 402. Det er et vanlig blandingspunkt: remove tar en verdi, pop tar en indeks.
.sort() returnerer None. Skriver du tall = tall.sort(), blir tall etterpå None, og neste linje stopper programmet. Metoden sorterte riktignok lista, men returnerte ingenting. Riktig er enten tall.sort() alene, eller sortert = sorted(tall). Det samme gjelder append, insert, remove og reverse.Feilkode #14 — å stjele et innebygd navn. Skriver du sum = 0 eller list = [1, 2], virker linja fint, men funksjonene sum(...) og list(...) er ødelagt resten av programmet. Det samme gjelder input, str, int, float, dict, set, max, min, len, type, range og format. Bruk total, verdier, tekst, tabell i stedet.
Ingen av de to gir en feilmelding der feilen står — den kommer flere linjer senere, og det er det som gjør dem dyre.
En resirkuleringsstasjon registrerer hvilken fraksjon hvert innlevert kolli havner i. Skriv funksjonen rydd(logg, fjern) som tar imot lista logg med fraksjonsnavn og ett navn fjern, og returnerer en NY liste der alle forekomster av fjern er borte. Originalen skal være uendret etterpå.
Test den med logg = ['glass', 'papir', 'glass', 'metall'] og fjern = 'glass'.
Løkke 5 — snarveiene, og løkka som gjør det samme (~8 min)
Nå kommer den delen der dette emnet skiller seg fra mange andre programmeringskurs: du får lov til å bruke de innebygde snarveiene. Løsningsforslagene gjør det selv.
Men du skal kunne begge deler, av en praktisk grunn: max(x) gir deg den største verdien, ikke hvem den tilhører. Skal du finne den dyreste varen OG navnet på den, må du gå gjennom lista selv.
Gir henholdsvis den største og den minste verdien i lista. På tekstlister sammenlignes ordene alfabetisk, med store bokstaver før små.
Er lista tom, stopper programmet med ValueError. Derfor sjekker robust kode if len(x) > 0: før den bruker dem.
Legger sammen alle tallene i lista og gir summen. En tom liste gir 0, ikke feil — det er den eneste av snarveiene som tåler tom liste.
sum virker bare på tall. Er elementene lest fra fil, er de strenger, og du må konvertere dem med int() eller float() først.
Gir en NY liste med elementene i stigende rekkefølge, og lar x stå urørt. sorted(x, reverse=True) gir synkende.
Dette er forskjellen fra x.sort(), som endrer originalen og returnerer None. Trenger du den gamle rekkefølgen senere, er sorted det trygge valget.
Gir True hvis verdien v finnes i lista x, ellers False. Den brukes både som betingelse og som vakt: if navn not in resultat: er standardmåten å unngå duplikater på.
Testen ser på verdier, ikke indekser, og den stopper aldri programmet — den svarer bare False når verdien mangler.
a + b gir en ny liste der b er skjøtt bakpå a; ingen av originalene endres. [0] * 4 gir [0, 0, 0, 0] — en rask måte å lage ei liste med startverdier på.+ legger ikke sammen tallene elementvis: [1, 2] + [3] blir [1, 2, 3], ikke [4, 2]. Det er en fast felle på kodesporing.
range gir en oppskrift på en tallrekke, ikke ei liste. list(...) gjør den om til ei ekte liste du kan skrive ut og indeksere.Skriver du print(range(3)), får du range(0, 3) og ikke [0, 1, 2]. Skal tallrekka vises, må den pakkes i list(...).
b = a gir IKKE en kopi — det gir et nytt navn på den samme lista, og en endring gjennom det ene navnet synes gjennom det andre.Vil du ha en uavhengig kopi, skriver du b = a[:] eller b = list(a). Dette er feilkode #15, aliasing, og den er vanskelig å se fordi koden ser riktig ut.
En prisliste for en dagligvarehandel ligger som to lister: varer med navn og priser med kroner, i samme rekkefølge. Finn den høyeste prisen, og finn hvilken vare den tilhører.
Den høyeste prisen alene er en énlinjes snarvei.
varer = ['brød', 'melk', 'kaffe', 'ost']
priser = [39, 25, 89, 61]
print(max(priser))Utskrift:
89Skal du også ha navnet, holder ikke max alene. Da går du gjennom indeksene og husker hvor det beste sto:
varer = ['brød', 'melk', 'kaffe', 'ost']
priser = [39, 25, 89, 61]
beste = 0
for i in range(len(priser)):
if priser[i] > priser[beste]:
beste = i
print(varer[beste], priser[beste])Utskrift:
kaffe 89En tredje vei er å bruke snarveien til å finne verdien og .index til å finne plassen:
varer = ['brød', 'melk', 'kaffe', 'ost']
priser = [39, 25, 89, 61]
topp = max(priser)
print(varer[priser.index(topp)], topp)Utskrift:
kaffe 89Alle tre gir full pott. Snarveien er kortest når du bare skal ha verdien; løkka er den du trenger når svaret er «hvem», og den er også den eneste som lett kan utvides til å håndtere at lista er tom. Merk at løkkevarianten starter med beste = 0, ikke med en oppdiktet minsteverdi — da slipper du å gjette hvor lave prisene kan bli.
En værstasjon har døgnmiddeltemperaturer for en uke i lista grader. Skriv over_snittet(grader) som returnerer ei liste med de temperaturene som er høyere enn ukesgjennomsnittet.
a) Løs oppgaven med snarveiene sum og len.
b) Forklar med én setning hvorfor funksjonen må regne ut gjennomsnittet FØR løkka, ikke inne i den.
Test med grader = [4, 9, 2, 7, 5, 11, 1].
Hva er forskjellen på b = a og b = a[:] når du etterpå endrer b?
Prøv begge deler i samme program.
a = [1, 2, 3]
b = a
b.append(99)
print(a)
c = [1, 2, 3]
d = c[:]
d.append(99)
print(c)Utskrift:
[1, 2, 3, 99]
[1, 2, 3]Første utskrift er poenget: a ble endret selv om du bare rørte b. b = a lager ikke en ny liste, bare et nytt navn på den gamle. I det andre tilfellet er d en ekte kopi, og c står urørt.
Regelen å ta med seg: skal en funksjon la argumentet sitt være i fred, må den jobbe på liste[:] — ikke på liste.
Hva skriver programmet ut? Skriv alle tre linjene nøyaktig.
a = [3, 6, 9, 12]
b = a
c = a[:]
b.append(15)
c.reverse()
print(a)
print(b)
print(c)Dette kapitlet er skrevet av Anthropics toppmodeller (Claude Opus og Claude Fable) og er foreløpig ikke manuelt gjennomgått — kvalitetskontrollen gjøres av uavhengige KI-agenter, og innmeldte feil rettes fortløpende. Funnet en feil? Meld fra, så retter vi den. Les mer om hvordan innholdet lages.
Skolesaga er en uavhengig læringsressurs og er ikke tilknyttet eller godkjent av Norges teknisk-naturvitenskapelige universitet. Dette er ikke offisielt studiemateriell. Les mer.