1.2 Lister, indeksering og slicing
Lister og nøstede lister, positiv og negativ indeksering, slicing, og de mekaniske reglene bak matche-tabellene i sjanger B.
- Sjanger B — matche-tabell for indeksering og slicing i nøstede lister. Du får en liten tabell med uttrykk som data[antall[1]] i venstre kolonne, og skal parre hvert av dem med riktig verdi fra en nummerert liste. Den er et fast innslag i settene, og gir 0,5 poeng per rad, med maksimalt 2–3,5 poeng på oppgaven.
- Sjanger A — kodesporing («hva skriver dette programmet ut?»), som forekommer i 100 % av settene. Lister er en av de vanligste innpakningene: hva står i lista nå, og hvordan skriver Python den ut?
- Fellene fra dette kapitlet er godt dokumenterte: at slicing gir en liste og indeksering gir ett element, at slutten i et utsnitt er eksklusiv, og at + på to lister skjøter dem sammen i stedet for å legge dem sammen tall for tall.
Prioritet: dette må sitte (høyeste av bokas tre nivåer). Sjanger B er blant de mest forutsigbare poengene på hele eksamenen: reglene er få, mekaniske og de samme hvert år. Det er lærbart til full uttelling.
print skiller argumentene sine med ett mellomrom.Har du aldri programmert, er Programmering med Python en mykere inngang du kan bla i først.
Løkke 1 — Lista som datastruktur (~10 min)
Værstasjonen på Blindern måler temperaturen én gang i timen. Du har ikke bruk for 24 variabler med navn time1, time2 og så videre — du vil ha én ting som holder alle 24 tallene i rekkefølge, slik at du kan spørre «hva var den tredje målingen?» og «hvor mange målinger har jeg?».
Det er en liste. Den er den mest brukte datastrukturen i faget, og den er inngangen til nesten alt annet: løkker, rekker, tabeller og fillesing.
En ordnet samling av verdier, skrevet i hakeparenteser med komma mellom: [3, 7, 2]. Verdiene kalles elementer, og de kan være av ulike typer i samme liste: [1, 'to', 3.0].
To ting å merke seg med en gang. Rekkefølgen er en del av lista — [1, 2] og [2, 1] er ikke samme liste. Og Python skriver en liste ut med hakeparenteser og komma pluss mellomrom:
maalinger = [3, 7, 2]
print(maalinger)Utskrift:
[3, 7, 2]Den tomme lista skrives [].
Antall elementer i lista. len([3, 7, 2]) gir 3.
Merk sammenhengen med indeksene: en liste med len(x) elementer har gyldige indekser fra 0 til len(x) - 1. Den siste indeksen er altså ikke len(x), og det er kilden til den nest hyppigste feilmeldingen i faget.
Legger v til på slutten av lista, og gjør lista én lengre. Lista endres på plass — du skal ikke skrive x = x.append(v).
Gjør du det likevel, blir x lik None, for append returnerer ingenting. Det er en klassisk feil, og den er stum: programmet krasjer først på neste linje som bruker x.
maalinger = [3, 7]
maalinger.append(2)
print(maalinger, len(maalinger))Utskrift:
[3, 7, 2] 3Sant hvis verdien v finnes som element i lista x. 7 in [3, 7, 2] er True.
Testen ser bare på hele elementer, ikke på deler av dem, og den sier ingenting om hvor verdien står. not in finnes også og betyr det motsatte.
Tre målinger kommer inn etter hverandre. Hva skriver programmet ut?
maalinger = [12, 15]
maalinger.append(9)
print(maalinger)
print(len(maalinger))
print(15 in maalinger, 13 in maalinger)Utskrift:
[12, 15, 9]
3
True FalseFørste linje ut er hele lista, skrevet slik Python gjør det: hakeparenteser, komma og mellomrom etter hvert komma. Skriver du [12,15,9] som svar i en sporingsoppgave, er det feil utskrift.
Andre linje er lengden etter at append har lagt til ett element. Tredje linje er to sannhetsverdier på samme linje, skilt av mellomrommet print setter inn.
(Innstegsoppgave — ren gjengivelse.) La verdier = [4, 9, 4, 1].
a) Hva gir len(verdier)?
b) Hva gir 9 in verdier?
c) Hva gir len(verdier) etter at verdier.append(0) er kjørt?
d) Skriv nøyaktig hvordan print(verdier) viser den opprinnelige lista på skjermen.
Løkke 2 — Indeksering: å plukke ut ett element (~8 min)
Nå skal du hente ut den tredje målingen. I Python gjør du det med hakeparentes og et tall — men tellingen begynner på null, og det er verdt å venne seg til med en gang.
Gir elementet på plass i. Tellingen starter på 0, så x[0] er det første elementet og x[2] er det tredje.
Resultatet er selve elementet, med den typen elementet har. Er x en liste med tall, er x[0] et tall — ikke en liste. Er indeksen utenfor lista, stopper programmet med en IndexError.
Teller fra slutten. x[-1] er det siste elementet, x[-2] det nest siste.
Det finnes ingen x[-0] som betyr «siste» — -0 er 0, altså det første elementet. Negativ indeksering er den idiomatiske måten å nå slutten av en liste uten å regne med len: x[-1] er penere og tryggere enn x[len(x) - 1].
Bytter ut elementet på plass i med v. Lista endres på plass, og lengden er uendret.
At dette er lovlig, er det som gjør lister mutable (endringsvennlige). Strenger er det motsatte: tekst[0] = 'A' stopper programmet.
Fem timesmålinger. Hva skriver programmet ut?
grader = [12, 15, 9, 11, 14]
print(grader[0], grader[2])
print(grader[-1], grader[-2])
print(grader[len(grader) - 1])
grader[1] = 16
print(grader)Utskrift:
12 9
14 11
14
[12, 16, 9, 11, 14]- grader[0] er 12, altså den første målingen. grader[2] er 9 — den tredje, ikke den andre.
- grader[-1] er 14 (siste) og grader[-2] er 11 (nest siste).
- grader[len(grader) - 1] er grader[4], altså det samme som grader[-1]. Den lange formen er unødvendig, men den er verdt å kjenne igjen.
- Etter grader[1] = 16 er det andre elementet byttet ut, og lengden er den samme.
Regelen å ha i hodet: indeksen er antall skritt fra starten, ikke plassnummeret. Første element ligger null skritt inn.
Har lista fem elementer, er x[5] ulovlig — gyldige indekser er 0 til 4. Da stopper programmet:
grader = [12, 15, 9, 11, 14]
print(grader[5])Utskrift: ingen (programmet stopper med en feilmelding før noe blir skrevet). Siste linje i feilmeldingen er:
IndexError: list index out of rangeFeiltypen heter IndexError, og den er en av de faste kandidatene i exception-matche-tabellene i kap. 2.2. Legg merke til at grensen er len(x) - 1, ikke len(x). Dette er slektningen til feilkode #1 (av-én-feilen), som er den hyppigste feilen i hele faget.
(Midtveisnivå, sjanger A — kodesporing, altså «hva skriver programmet ut?».) Hva skriver programmet ut?
nedbor = [0, 4, 12, 3]
print(nedbor[1], nedbor[-1])
print(nedbor[0] + nedbor[-2])Løkke 3 — Slicing: å plukke ut et utsnitt (~12 min)
Nå vil du ha de tre første målingene, ikke bare én. Det kalles slicing (utsnitt), og det er her sjanger B tjener pengene sine. Reglene er få, men de må være helt presise.
Gir en ny liste med elementene fra plass a til og med plass b - 1. Slutten er eksklusiv: b er den første plassen som ikke blir med.
Konsekvensen er praktisk: x[a:b] har b - a elementer. x[1:4] gir tre elementer, uansett hvor lang lista er. Utelater du a, begynner utsnittet på starten (x[:3]); utelater du b, går det til slutten (x[2:]). Utsnittet er en ny liste — den opprinnelige er urørt.
Dette er kapitlets viktigste enkeltsetning. x[2] gir elementet på plass 2. x[2:3] gir en liste med ett element — nemlig det samme elementet, pakket i hakeparenteser.
For x = [12, 15, 9] er x[2] altså 9, mens x[2:3] er [9]. På en matche-tabell er 9 og [9] to forskjellige svaralternativer, og det er hele poenget med raden.
Konsekvens: du kan regne med x[2] + 1, men x[2:3] + 1 stopper programmet, for du kan ikke legge et tall til en liste.
Tredje tallet er steget: x[0:6:2] tar hvert andre element fra plass 0 til og med plass 5. Standardsteget er 1.
Alle tre tallene kan utelates. x[::2] er hvert andre element i hele lista, og x[1::2] er hvert andre fra plass 1 — altså elementene på oddetallsplassene.
Negativt steg snur retningen. x[::-1] gir lista baklengs, som en ny liste.
Det er det idiomatiske uttrykket for «reverser», og det brukes blant annet til å sjekke om en tekst er et palindrom: tekst == tekst[::-1]. Merk at den opprinnelige lista ikke endres.
Utsnittet uten grenser gir en ny liste med samme innhold. Det er den vanligste måten å kopiere en liste.
Grunnen til at du trenger den, kommer i neste løkke: b = x gir deg ikke en kopi, men et nytt navn på den samme lista.
Seks målinger. Hva skriver programmet ut, og hvilke av linjene gir en liste?
grader = [12, 15, 9, 11, 14, 8]
print(grader[1:4])
print(grader[:3])
print(grader[3:])
print(grader[2], grader[2:3])
print(grader[::2])
print(grader[::-1])Utskrift:
[15, 9, 11]
[12, 15, 9]
[11, 14, 8]
9 [9]
[12, 9, 14]
[8, 14, 11, 9, 15, 12]- grader[1:4] — start på plass 1, stopp før plass 4: tre elementer, [15, 9, 11].
- grader[:3] — fra starten, tre elementer.
- grader[3:] — fra plass 3 til slutten.
- grader[2] er tallet 9; grader[2:3] er lista [9]. Se forskjellen i utskriften: den ene har hakeparenteser, den andre ikke.
- grader[::2] — hvert andre element: plassene 0, 2, 4.
- grader[::-1] — hele lista baklengs.
Alle utsnittene er nye lister. grader selv er uendret hele veien, og det er derfor de seks linjene kan bruke den om og om igjen.
Feilkode #18 (bokas nummerering av vanlige feil, samlet i kap. 0.1) er tre feil i samme familie:
- Indeksering gir element, slicing gir liste. x[2] og x[2:3] er ikke samme svar.
- Slutten er eksklusiv. x[1:4] gir tre elementer, ikke fire. Vil du ha «til og med plass 4», må du skrive x[1:5].
- Negativ indeks teller fra -1. Det finnes ingen -0.
Én ting som ikke er en felle: et utsnitt utenfor lista gir ingen feilmelding. x[2:99] gir bare det som finnes, og x[9:12] gir den tomme lista []. Det er bare indeksering som kan gi IndexError, aldri slicing.
(Midtveisnivå, sjanger A.) Hva skriver programmet ut?
verdier = [5, 8, 2, 9, 4]
print(verdier[1:3])
print(verdier[-2:])
print(verdier[0], verdier[0:1])
print(verdier[3:99])Løkke 4 — Å skjøte, gjenta og kopiere lister (~8 min)
Nå kommer den fellen som EKSAMENSANALYSEN har som en av de aller vanligste: hva + gjør med to lister.
+ skjøter sammen: [1, 2] + [3, 4] gir [1, 2, 3, 4], altså en ny liste med fire elementer.Den legger ikke sammen tall for tall. Det finnes ingen [1, 2] + [3, 4] som gir [4, 6] i vanlig Python — den elementvise addisjonen får du først med NumPy-arrays i kap. 4.1, og det er nettopp derfor forskjellen er en så god eksamensfelle.
Begge operandene må være lister. [1, 2] + 3 stopper programmet.
x * n gir en ny liste der innholdet er gjentatt n ganger: [0] * 4 gir [0, 0, 0, 0], og [1, 2] * 2 gir [1, 2, 1, 2].[0] * n er den vanlige måten å lage en liste med n nuller når du skal fylle den etterpå. (For tallberegninger bruker boka np.zeros fra kap. 4.1 i stedet, men mønsteret er det samme.)
b = x lager ikke en kopi. Det lager et nytt navn på den samme lista, så en endring gjennom b er synlig gjennom x også.Vil du ha en uavhengig kopi, må du be om det: b = x[:] eller b = list(x). Dette er feilkode #23, og den er ubehagelig fordi programmet ikke gir noen feilmelding — det gir bare et annet svar enn du trodde.
Hva skriver programmet ut? Legg spesielt merke til de to siste linjene.
a = [1, 2]
b = [3, 4]
print(a + b)
print(a * 2)
print(len(a + b))
c = a
c.append(99)
print(a, c)
d = a[:]
d.append(0)
print(a, d)Utskrift:
[1, 2, 3, 4]
[1, 2, 1, 2]
4
[1, 2, 99] [1, 2, 99]
[1, 2, 99] [1, 2, 99, 0]- a + b skjøter og gir fire elementer. Hadde dette vært NumPy-arrays, ville svaret vært [4, 6] — her er det [1, 2, 3, 4].
- a * 2 gjentar innholdet.
- len(a + b) er 4, altså summen av lengdene.
- c = a gir to navn på samme liste. Etter c.append(99) inneholder begge [1, 2, 99], for det finnes bare én liste.
- d = a[:] gir en ekte kopi. Etter d.append(0) er a uendret.
Sammenlign de to siste utskriftene nøye: det er hele forskjellen mellom aliasing og kopi, og den er verdt å ha sett én gang før eksamen.
(Midtveisnivå, sjanger A — liste-konkatenering mot tallsum.) Hva skriver programmet ut?
x = [2, 3]
y = [4]
print(x + y)
print(x + y + y)
print([0] * 3)
print(x[0] + y[0])Løkke 5 — Nøstede lister og indeks inne i indeks (~12 min)
— naturlig pausepunkt —
Nå er du framme ved sjanger B. En nøstet liste er en liste der elementene selv er lister — for eksempel én liste per målestasjon, med stasjonens målinger inni. Det er slik en tabell ser ut i Python før NumPy kommer inn i bildet.
Er elementene i y selv lister, henter y[i] hele den indre lista, og y[i][j] henter element j inne i den.
Les uttrykket fra venstre mot høyre: y[1][2] betyr «gå til den andre indre lista, og hent det tredje elementet der». De indre listene behøver ikke være like lange — [[9, 8], [7, 6, 5], [2]] er en helt vanlig nøstet liste, og det gjør at len(y[i]) varierer med i.
len(y) er antall indre lister, ikke antall tall til sammen.
Et uttrykk kan bruke en verdi fra én liste som indeks i en annen. Regelen er den samme som i regning: den innerste parentesen først.
x[y[0]] betyr: regn ut y[0], som må bli et heltall, og bruk det som indeks i x. Med x = [5, 8, 2] og y = [2, 0] blir y[0] lik 2, og x[2] lik 2.
Dette er selve konstruksjonen sjanger B tester. Metoden er alltid den samme: skriv om uttrykket i flere små steg, og skriv verdien over hvert steg.
Tre målestasjoner har hver sin liste med døgnnedbør, og en liste holder rekkefølgen stasjonene skal behandles i:
rekkefolge = [2, 0, 1]
data = [[12, 15], [9, 11, 14], [7]]Regn ut verdien av hvert av uttrykkene data[rekkefolge[0]], data[rekkefolge[1]][1:], rekkefolge[-1] + rekkefolge[rekkefolge[1]] og len(data[rekkefolge[2]]).
Metoden er å bygge ned uttrykket i små steg, og skrive verdien for hvert steg. Aldri prøv å gjøre det i ett hopp.
Rad 1: data[rekkefolge[0]]
| Steg | Uttrykk | Verdi |
|---|---|---|
| 1 | rekkefolge[0] | 2 |
| 2 | data[2] | [7] |
Svaret er lista
[7] — ikke tallet 7, for indeksering i en nøstet liste gir den indre lista.Rad 2:
data[rekkefolge[1]][1:]| Steg | Uttrykk | Verdi |
|---|---|---|
| 1 | rekkefolge[1] | 0 |
| 2 | data[0] | [12, 15] |
| 3 | [12, 15][1:] | [15] |
Utsnittet starter på plass 1 og går til slutten, altså ett element.
Rad 3: rekkefolge[-1] + rekkefolge[rekkefolge[1]]
| Steg | Uttrykk | Verdi |
|---|---|---|
| 1 | rekkefolge[-1] | 1 |
| 2 | rekkefolge[1] (innerst i det andre leddet) | 0 |
| 3 | rekkefolge[0] | 2 |
| 4 | 1 + 2 | 3 |
Her er begge leddene tall, så
+ er vanlig addisjon.Rad 4:
len(data[rekkefolge[2]])| Steg | Uttrykk | Verdi |
|---|---|---|
| 1 | rekkefolge[2] | 1 |
| 2 | data[1] | [9, 11, 14] |
| 3 | len([9, 11, 14]) | 3 |
Til slutt kontrollerer vi hele oppgaven ved å la Python svare:
rekkefolge = [2, 0, 1]
data = [[12, 15], [9, 11, 14], [7]]
print(data[rekkefolge[0]])
print(data[rekkefolge[1]][1:])
print(rekkefolge[-1] + rekkefolge[rekkefolge[1]])
print(len(data[rekkefolge[2]]))Utskrift:
[7]
[15]
3
3Hva som gir uttelling: på en matche-tabell er hver rad 0,5 poeng og de rettes uavhengig. Får du tre av fire rader, får du tre halve poeng — det er ingen alt-eller-ingenting-oppgave. Derfor lønner det seg alltid å regne ut de radene du er trygg på, og gjette på resten (blankt og feil gir likt).(Midtveisnivå, sjanger B — matche-tabell der du regner ut indekserings- og utsnittsuttrykk og parrer dem med verdier.) La
plass = [1, 3, 0]
tabell = [[6, 2], [8], [4, 5, 7], [3, 9]]Regn ut hvert uttrykk i venstre kolonne, og finn riktig verdi blant alternativene under.
| Uttrykk |
|---|
| tabell[plass[0]] |
| tabell[plass[2]][1] |
| tabell[plass[1]][::-1] |
| plass[0] + plass[1] |
Alternativer:
(1) [9, 3] ·
(2) 2 ·
(3) [8] ·
(4) 4 ·
(5) [3, 9] ·
(6) [6, 2]
Hva skriver programmet ut?
rader = [[1, 2], [3, 4]]
rader[0].append(9)
rader.append([5])
print(rader)
print(len(rader), len(rader[0]))
print(rader[-1][0] + rader[0][-1])Løkke 6 — Å lage lister av tallrekker (~7 min)
Til sist to snarveier du trenger allerede nå, selv om de hører hjemme i kap. 1.3: måten å lage en liste med mange tall uten å skrive dem alle.
range er en oppskrift på en tallrekke. range(2, 7) betyr «fra og med 2, til men ikke med 7». range(5) er kortformen for range(0, 5), og et tredje tall er steget: range(0, 10, 3).range skriver seg ikke ut som en liste — print(range(5)) gir range(0, 5). Vil du se tallene, må du lage en liste: list(range(5)) gir [0, 1, 2, 3, 4].
Antall verdier er stop - start når steget er 1. Det gir den viktigste enkeltregelen i faget: range(N) gir N verdier, og range(N+1) gir N+1 verdier. Løkkene i kap. 1.3 bygger på dette.
En kompakt måte å bygge en liste: [3*e for e in range(4)] gir [0, 3, 6, 9]. Les den som «tre ganger e, for hver e i tallrekka».
Den gjør samme jobb som en løkke med append, på én linje. Boka bruker den bare i denne enkle formen — den store gjennomgangen av løkker kommer i kap. 1.3, og der ser du også når en vanlig løkke er den beste besvarelsen.
sum(x) legger sammen alle tallene i lista, min(x) gir det minste og max(x) det største.De virker bare på lister av verdier som kan sammenlignes eller legges sammen — sum([[1], [2]]) stopper programmet, for du kan ikke legge en liste til et tall. Og husk fra advarselen tidligere: bruk aldri sum som variabelnavn, ellers mister du funksjonen.
Hva skriver programmet ut?
print(range(4))
print(list(range(4)))
print(list(range(2, 7)))
print(list(range(0, 10, 3)))
kvadrater = [e*e for e in range(5)]
print(kvadrater)
print(sum(kvadrater), min(kvadrater), max(kvadrater))Utskrift:
range(0, 4)
[0, 1, 2, 3]
[2, 3, 4, 5, 6]
[0, 3, 6, 9]
[0, 1, 4, 9, 16]
30 0 16- print(range(4)) skriver oppskriften, ikke tallene. Det er en yndet felle.
- list(range(4)) gir fire tall, fra 0. Ikke fem — 4 er ikke med.
- list(range(2, 7)) gir 7 - 2 = 5 tall.
- list(range(0, 10, 3)) hopper tre av gangen og stopper før 10.
- [e*e for e in range(5)] gir kvadratene av 0 til 4.
- sum, min og max på den lista gir 30, 0 og 16.
Legg merke til antallet i hvert tilfelle. Det er feilkode #1 — av-én-feilen — i sin reneste form, og den er den hyppigste feilen i hele faget.
(Midtveisnivå, sjanger A.) Hva skriver programmet ut?
tall = list(range(1, 6))
print(tall)
print(len(tall), sum(tall))
print(tall[1:4])
print([2*e for e in tall][-1])La
n = [3, 0, 2]
m = [[1, 4, 6], [2], [5, 8], [7, 9, 0, 3]]Regn ut hvert uttrykk og finn riktig verdi blant alternativene.
| Uttrykk |
|---|
| m[n[0]][1:3] |
| m[n[1]][-1] |
| len(m[n[2]]) + n[2] |
| m[n[2]][0:1] |
| n[n[1]] |
Alternativer:
(1) 4 ·
(2) [9, 0] ·
(3) [5] ·
(4) 6 ·
(5) 3 ·
(6) [5, 8] ·
(7) 2
Hva skriver programmet ut?
a = [1, 2, 3]
b = a
c = a[:]
b[0] = 99
c[1] = 77
print(a)
print(b)
print(c)
print(a is b, a is c, a == c)- #18 — indekser og utsnitt. Indeksering gir element, slicing gir liste; slutten i et utsnitt er eksklusiv; negativ indeks teller fra -1. Dette er kapitlets hovedfelle.
- #5 — liste-konkatenering mot elementvis sum. [1, 2] + [3, 4] er [1, 2, 3, 4], ikke [4, 6]. Den elementvise summen kommer først med NumPy i kap. 4.1.
- #1 — av-én. range(a, b) gir b - a verdier, og gyldige indekser i en liste med n elementer er 0 til n - 1.
- #23 — aliasing. b = a gir et nytt navn på samme liste. Kopi krever a[:] eller list(a).
- #16 — utskriftsdetaljer. Python skriver lister med mellomrom etter kommaet: [1, 2, 3].
- x = x.append(v). append returnerer ingenting, så x blir None. Skriv bare x.append(v).
Begrepsbank
Begrepsbanken er flashcard-/repetisjonsstoff — den gjentar det du nettopp har lest. Hopp trygt over ved førstegangslesing; tidsanslaget for kapitlet gjelder kjernestoffet.
En liste kan endres etter at den er laget: du kan bytte ut et element (x[0] = 5), legge til (x.append(v)) og endre en indre liste i en nøstet struktur.
Det er praktisk, men det er også grunnen til at aliasing biter: to navn på samme mutable liste ser hverandres endringer. Strenger er derimot immutable — de kan ikke endres, bare erstattes. Se kap. 1.4.
Slicing feiler aldri. x[3:99] gir bare det som finnes, og x[9:12] på en kort liste gir den tomme lista [].
Det er en systematisk forskjell fra indeksering, som gir IndexError med en gang indeksen er for stor. På en matche-tabell er «IndexError» derfor et distraktoralternativ som aldri er riktig for et utsnitt.
[] har lengde 0, ingen gyldige indekser, og regnes som falsk i en ja/nei-test. Derfor er if verdier: det idiomatiske uttrykket for «hvis lista ikke er tom».sum([]) er 0, som er fornuftig, men min([]) og max([]) stopper programmet — det finnes ikke noe minste element i en tom samling.
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 Universitetet i Oslo. Dette er ikke offisielt studiemateriell. Les mer.