CMD + K

Informasjonsteknologi grunnkursDictionaries og mengder (sets)Begreper & formler5
6 min lesing1 video

Dictionaries og mengder (sets)

En dictionary lagrer par av nøkkel og verdi og gir lynraskt oppslag på nøkkel. En mengde (set) holder unike elementer uten rekkefølge. Begge er sentrale verktøy når du skal telle, gruppere eller fjerne duplikater i data.

Læringsmål
  • 01Opprette en dictionary med nøkkel-verdi-par og hente, legge til, endre og fjerne par via nøkkelen
  • 02Bruke get med standardverdi og in for å unngå KeyError, og forklare hvorfor nøkler må være uforanderlige
  • 03Løpe gjennom en dictionary med items, keys og values, og bygge en telle-struktur med get
  • 04Forklare hva et set er, bruke det til å fjerne duplikater og teste medlemskap, og velge mellom liste, dictionary og set til en gitt oppgave

Når posisjon ikke holder

Tenk deg at du fører en telefonliste. Med en liste måtte du huske at Ada lå på indeks 0, Alan på indeks 1 og Grace på indeks 2 — og hver gang noen flyttet plass, raknet hele systemet. Det du egentlig vil, er å slå opp et navn og få nummeret rett ut, uten å tenke på posisjon. Det er nettopp dette en dictionary gir deg.

En dictionary lagrer par av en nøkkel og en verdi. Du skriver den med klammeparenteser og kolon mellom nøkkel og verdi:

python
telefon = {"Ada": "12345678", "Alan": "87654321", "Grace": "11223344"}

Her er "Ada", "Alan" og "Grace" nøkler, og telefonnumrene er de tilhørende verdiene. For å hente en verdi bruker du nøkkelen i klammeparentes, akkurat der du før brukte en indeks: telefon["Ada"] gir "12345678". Forskjellen fra en liste er avgjørende — du slår ikke opp på hvor noe ligger, men på hva det heter.

telefonnøkkel (navn)verdi (nummer)"Ada""12345678""Alan""87654321""Grace""11223344"Hver nøkkel peker entydig på sin verdi. Du slår opp telefon["Ada"] på navnet, ikke på en posisjon — det er navnet selv som er adressen.
FIGDictionary: navn peker på telefonnummer

Figuren viser hvordan hver nøkkel peker på sin verdi. Det finnes ingen «første» eller «andre» nøkkel i den forstand en liste har det; det er navnet selv som er adressen. En slik oppslag-struktur kalles på fagspråk en oppslagstabell eller et assosiativt array, fordi den knytter — assosierer — en nøkkel til en verdi.

Å lese, legge til og endre

Oppslag på en nøkkel som ikke finnes, krasjer programmet med en KeyError. Det er ofte uønsket, og derfor finnes en tryggere vei: telefon.get("Bjarne") gir None i stedet for å krasje når nøkkelen mangler, og telefon.get("Bjarne", "ukjent") lar deg oppgi en standardverdi som returneres i stedet. Når du er usikker på om en nøkkel finnes, er get nesten alltid det riktige valget.

Å legge til eller endre et par gjøres med samme skrivemåte:

python
telefon["Bjarne"] = "55667788" # nytt par legges til telefon["Ada"] = "99999999" # eksisterende verdi endres

Legg merke til at den samme linja både oppretter et nytt par og overskriver et eksisterende — Python ser på om nøkkelen finnes fra før. En dictionary er altså foranderlig, akkurat som en liste: du kan vokse, krympe og endre den under kjøring. For å fjerne et par bruker du del telefon["Alan"], eller telefon.pop("Alan") som i tillegg returnerer verdien du fjernet.

Vil du sjekke om en nøkkel finnes før du slår opp, bruker du in: "Ada" in telefon gir True. Merk at in ser på nøklene, ikke verdiene. Dette er en rask og vanlig måte å unngå KeyError på.

dict-oppslag

Oppslag, get og telling i en dictionary

python
telefon = {"Ada": "12345678", "Alan": "87654321"} # Direkte oppslag — krasjer med KeyError hvis nøkkelen mangler:print(telefon["Ada"])              # "12345678" # Trygt oppslag med get — gir None eller en standardverdi i stedet:print(telefon.get("Bjarne"))       # Noneprint(telefon.get("Bjarne", "ukjent"))   # "ukjent" # Legge til, endre og sjekke:telefon["Bjarne"] = "55667788"     # nytt parprint("Ada" in telefon)            # True (in ser på nøklene) # Telle forekomster med get og en standardverdi på 0:tekst = "abracadabra"antall = {}for tegn in tekst:    antall[tegn] = antall.get(tegn, 0) + 1print(antall)   # {"a": 5, "b": 2, "r": 2, "c": 1, "d": 1}

Bruk klammeparentes når nøkkelen helt sikkert finnes, og get når du er i tvil — get krasjer aldri. Telle-mønsteret antall[x] = antall.get(x, 0) + 1 er en av de mest brukte idiomene i hele Python.

Snutten over samler de operasjonene du kommer til å bruke oftest. Studer særlig forskjellen mellom direkte oppslag med klammeparentes, som kan krasje, og get, som ikke gjør det. Et godt vett-regel: bruk klammeparentes når du er sikker på at nøkkelen finnes, og get når du er i tvil.

Regler for nøkler

Ikke hva som helst kan være en nøkkel. En nøkkel må være uforanderlig — derfor kan du bruke strenger, tall og tupler som nøkler, men ikke lister. Grunnen er teknisk men verdt å forstå: Python regner ut en slags tall-signatur av nøkkelen for å finne verdien lynraskt, og den signaturen må holde seg fast. En liste kan endres, og da ville signaturen sprike, så Python nekter.

Verdiene har derimot ingen slike begrensninger. En verdi kan være hva som helst — et tall, en streng, en liste, ja til og med en annen dictionary. Det er svært vanlig at verdien er en liste, for eksempel hvis du vil lagre flere telefonnumre per person.

En annen viktig regel er at nøklene er unike. Skriver du samme nøkkel to ganger, vinner den siste; det blir ikke to par med samme nøkkel. Det henger sammen med hele ideen: en nøkkel skal peke entydig på én verdi.

Å løpe gjennom en dictionary

Som med lister og strenger kan du gå gjennom en dictionary med en for-løkke. Standardformen gir deg nøklene, én om gangen:

python
for navn in telefon: print(navn, telefon[navn])

Ofte er det ryddigere å be om både nøkkel og verdi samtidig med items(), som gir deg paret i hver runde:

python
for navn, nummer in telefon.items(): print(f"{navn}: {nummer}")

Du kan også be om bare nøklene med keys() eller bare verdiene med values(). Et klassisk bruksmønster er å telle forekomster: gå gjennom en tekst eller liste, og bruk hvert element som nøkkel i en dictionary der verdien er antallet. Da kombinerer du get med en standardverdi på en elegant måte, slik snutten over viser med ord-tellingen.

Et lite forbehold er verdt å nevne: rekkefølgen du går gjennom en dictionary i, er den rekkefølgen parene ble lagt inn. Det var ikke alltid slik — i eldre Python var en dictionary helt uordnet — men i moderne Python kan du regne med innsettingsrekkefølgen. Likevel bør du ikke bruke den rekkefølgen til noe viktig, for poenget med en dictionary er nettopp at du slår opp på nøkkel, ikke på posisjon. Trenger du en bestemt sortering, sorterer du nøklene eksplisitt med sorted(telefon) når du går gjennom.

Mengder — når bare medlemskap teller

En nær slektning av dictionary er set (mengde), en mengde. Et set er en uordnet samling av unike verdier, uten noen tilknyttet verdi — du kan tenke på det som en dictionary med bare nøkler. Du skriver det med klammeparenteser, men uten kolon:

python
frukt = {"eple", "banan", "eple", "pære"} print(frukt) # {"eple", "banan", "pære"} — duplikatet er borte

Det mest karakteristiske ved et set er nettopp at det fjerner duplikater automatisk. Legger du til en verdi som allerede er der, skjer ingenting. Dette gjør set perfekt når du vil vite hvilke unike verdier som finnes i en samling: set(["a", "b", "a", "c"]) gir {"a", "b", "c"}. En vanlig idiom er len(set(liste)) for å telle hvor mange forskjellige verdier en liste inneholder.

Den andre styrken er lynraske medlemskaps-tester. "eple" in frukt er svært rask, fordi et set bruker samme tall-signatur-triks som en dictionary i stedet for å lete element for element slik en liste må. Skal du sjekke medlemskap mange ganger i en stor samling, er et set langt raskere enn en liste. Forskjellen merkes lite på ti elementer, men blir dramatisk på hundretusener: der en liste må bla seg gjennom alt for å avgjøre om en verdi finnes, går et set rett på svaret nesten uavhengig av hvor stort det er.

Set støtter også de matematiske mengdeoperasjonene du kjenner fra skolen. Union (|) gir alt som er i minst én av to mengder, snitt (&) gir det som er i begge, og differanse (-) gir det som er i den første men ikke den andre. Vil du finne hvilke kunder som handlet både i januar og februar, tar du snittet av to mengder kunde-navn.

Vær oppmerksom på én snublestein: en tom mengde må skrives set(), ikke {}. Tomme klammeparenteser lager nemlig en tom dictionary, ikke et tomt set — det er den ene gangen de to skrivemåtene kolliderer.

Når velger du hva?

Velg en dictionary når du vil slå opp en verdi via en meningsfull nøkkel — navn til nummer, varekode til pris, brukernavn til profil. Velg et set når du bare bryr deg om hvilke verdier som finnes, vil fjerne duplikater, eller trenger raske medlemskaps-tester uten noen tilhørende verdi. Og velg fortsatt en liste når rekkefølgen betyr noe og du slår opp på posisjon. De tre dekker hver sin form for «samling», og å kjenne igjen hvilken form en oppgave krever, er halve jobben med å løse den ryddig.

Mine notater

Skriv egne notater for dette kapittelet. De lagres automatisk og dukker opp i «Mine notater»-oversikten paa fag-siden.

Laster…