Kopiere dictionaries

I artikkelen om å kopiere lister ser vi at når vi tilordner ei liste til en annen variabel, blir det ikke utført noen kopiering av innholdet, den andre variabelen blir bare et nytt navn på den samme lista. Det samme er tilfelle med dictionaries.

For å illustrere bruker vi koden fra eksempel 1 i artikkelen om å kombinere dictionaries med dictionaries og lister:

agentbil = {
    "eier": "James Bond",
    "merke": "Aston Martin",
    "motordata": {"effekt": 125, "slagvolum": 2.1, "dreiemoment": 225},
    "dekkdata": {"bredde": 155, "profil": 70, "felg": 12}
}

Så skriver vi

nybil = agentbil

Da blir nybil en referanse til samme dictionary som agentbil, som vi kan se i Python Tutor:

Illustrasjon av flere referanser til samme dictionary

På samme måte som lister, har også dictionaries en copy()-metode, som genererer en ny dictionary. Hvis vi skriver

nybil = agentbil.copy()

blir nybil en ny dictionary, som vi kan se i Python Tutor:

Illustrasjon av grunn kopi av dictionary

Vi ser imidlertid at dictionaries med nøkler "motordata" og "dekkdata" inni agentbil ikke er blitt kopiert, det er bare gjort en grunn kopiering. Vil vi gjøre en dyp kopiering, kan vi bruke funksjonen deepcopy() fra modulen copy, på samme måte som med lister, slik det beskrives i artikkelen om grunn og dyp kopiering. Skriver vi

from copy import deepcopy
 
nybil = deepcopy(agentbil)

blir også dictionaries inni agentbil kopiert, som vi kan se i Python Tutor:

Illustrasjon av dyp kopi av dictionary

Kilder

    • Sweigart A. (2021). Beyond the basic stuff with Python. no starch press

Kombinere dictionaries med dictionaries og lister

Akkurat som vi kan ha lister inni lister, kan vi ha dictionaries inni dictionaries. Eksempel 1 har en dictionary som heter agentbil, der verdiene til nøklene "motordata" og "dekkdata" er egne dictionaries.

Eksempel 1:

agentbil = {
    "eier": "James Bond",
    "merke": "Aston Martin",
    "motordata": {"effekt": 125, "slagvolum": 2.1, "dreiemoment": 225},
    "dekkdata": {"bredde": 155, "profil": 70, "felg": 12}
}

I Python Tutor ser det slik ut:

Illustrasjon av dictionary i dictionary

For å referere til en nøkkel i en dictionary inni en dictionary må vi bruke 2 indekser, én for hver dictionary. For eksempel agentbil["dekkdata"]["bredde"] for å finne dekkbredden til agentbil i eksempel 1.

Vi kan også ha dictionaries i lister. Eksempel 2 har ei liste som inneholder dictionaries som kopler postnummer og sted.

Eksempel 2:

postinfo = [
    {"nummer": 4225, "navn": "Lyngdal"},
    {"nummer": 4586, "navn": "Korshamn"},
    {"nummer": 4590, "navn": "Snartemo"},
    {"nummer": 4596, "navn": "Eiken"}
]

I Python Tutor ser det slik ut:

Illustrasjon av dictionary i liste

For å referere til en nøkkel i en dictionary inni ei liste, må vi bruke 2 indekser, én for lista og én for dictionary. For eksempel vil postinfo[1]["navn"] i eksempel 2 være "Korshamn" fordi element nummer 1 i postinfo inneholder en dictionary med nøkkel-verdi-paret "navn": "Korshamn".

Koden under vil gjennomløpe lista i eksempel 2 og skrive ut informasjon om postnummer og navn.

for sted in postinfo:
    print(f"{sted['navn']} har postnummer {sted['nummer']}.")

Vi kan også lister i dictionaries. Eksempel 3 inneholder en dictionary med forfattere som nøkler og lister med boktitler som verdier.

Eksempel 3:

katalog = {
    "Knut Hamsun": ["Sult", "Markens grøde", "Victoria"],
    "Ernest Hemingway": ["Og solen går sin gang", "Den gamle mannen og havet"],
    "Roald Dahl": ["Charlie og sjokoladefabrikken", "Heksene", "Matilda", "SVK"]
}

I Python Tutor ser det slik ut:

Illustrasjon av lister i dictionary

For å referere til et element i ei liste inni en dictionary, må vi bruke 2 indekser, én for dictionary og én for lista. For eksempel vil katalog["Roald Dahl"][1] i eksempel 3 være "Heksene" fordi element nummer 1 i lista knyttet til nøkkel "Roald Dahl", inneholder "Heksene".

Koden under vil gjennomløpe lista i eksempel 3 og skrive ut informasjon om forfattere og bøker.

for forfatter, titler in katalog.items():
    print(f"{forfatter} har skrevet:", end = " ")
    for tittel in titler:
        print(f"'{tittel}'", end = " ")
    print()

Det er ingen teoretisk grense for hvor mange dictionaries og lister vi kan nøste inni hverandre, men i praksis blir det sjelden bruk for mer enn 2-3 nivåer.

Kilder

    • Matthes A. (2019). Python Crash Course. no starch press

Telle med dictionaries

I denne artikkelen skal vi utforske dictionaries gjennom et eksempel der vi teller forekomster av tegn i en streng. Vi skal skrive kode som går gjennom en streng og skriver ut antall forekomster av tegn i strengen, sortert alfabetisk. Med strengen "bare barn er barn, men alle er vi barnebarn", skal vi få skrevet ut en slik oversikt:
[(' ', 8), (',', 1), ('a', 6), ('b', 5), ('e', 6), ('i', 1), ('l', 2), ('m', 1), ('n', 5), ('r', 7), ('v', 1)]
 Altså informasjon om at strengen inneholder åtte blanke, ett komma, seks a-er, og så videre.

Vi utvikler koden skrittvis, og starter med kode som ikke gjør annet enn å skrive ut tegnene i strengen den får inn.

Skritt 1

Vi benytter oss av at vi kan bruke ei for-løkke til å hente ut ett og ett tegn fra en streng.

streng = "bare barn er barn, men alle er vi barnebarn"
for tegn in streng:
    print(f"'{tegn}'", end = " ")

Når vi gir inn end = " " til print(), betyr det at vi skal ha et blankt tegn etter hver utskrift, ikke linjeskift, slik det beskrives i artikkelen om å formatere utskrifter.

Når vi kjører koden, får vi skrevet ut

'b' 'a' 'r' 'e' ' ' 'b' 'a' 'r' 'n' ' ' 'e' 'r' ' ' 'b' 'a' 'r' 'n' ',' ' ' 'm' 'e' 'n' ' ' 'a' 'l' 'l' 'e' ' ' 'e' 'r' ' ' 'v' 'i' ' ' 'b' 'a' 'r' 'n' 'e' 'b' 'a' 'r' 'n' 

Skritt 2

Nå endrer vi koden slik at den legger tegnene inn i en dictionary i stedet for å skrive dem ut. Til dette bruker vi dictionary-metoden update(). Som det står i artikkelen om dictionaries, vil update() oppdatere hvis en nøkkel finnes fra før, legge til hvis ikke. I denne første versjonen teller vi ikke forekomster, vi registrerer bare et spørsmålstegn sammen med hver nøkkel. Til slutt skriver vi dictionary ut.

streng = "bare barn er barn, men alle er vi barnebarn"
antall = {}  # Tom dictionary
for tegn in streng:
    antall.update({tegn: "?"})
print(antall)

Når vi kjører koden, får vi skrevet ut

{'b': '?', 'a': '?', 'r': '?', 'e': '?', ' ': '?', 'n': '?', ',': '?', 'm': '?', 'l': '?', 'v': '?', 'i': '?'} 

Skritt 3

Nå begynner vi å bruke dictionary til å telle forekomster av tegn, i stedet for å bare legge inn spørsmålstegn. Vi sjekker om et tegn ligger i dictionary fra før. Hvis det gjør, leser vi hvor mange ganger forekomster det har så langt, og øker denne med 1. Hvis et tegn ikke ligger i dictionary fra før, registrerer vi det med 1 forekomst.

streng = "bare barn er barn, men alle er vi barnebarn"
antall = {}  # Tom dictionary
for tegn in streng:
    if tegn in antall:
        tidl = antall.get(tegn)  # Antall tidligere forekomster
        antall.update({tegn: tidl + 1})  # Øk antall forekomster med 1
    else:
        antall.update({tegn: 1})  # Registrer 1 forekomst
print(antall)

Når vi kjører koden, får vi skrevet ut

{'b': 5, 'a': 6, 'r': 7, 'e': 6, ' ': 8, 'n': 5, ',': 1, 'm': 1, 'l': 2, 'v': 1, 'i': 1}

Skritt 4

Nå fungerer koden. Vi kan imidlertid forenkle den mye ved å benytte oss av at vi i dictionary-metoden get() kan angi hva vi vil ha returnert hvis en nøkkel ikke finnes i dictionary. Dette lar vi så være 0.

streng = "bare barn er barn, men alle er vi barnebarn"
antall = {} # Tom dictionary
for tegn in streng:
    # Øk antall forekomster med 1
    tidl = antall.get(tegn, 0)  
    antall.update({tegn: tidl + 1})
print(antall)

Vi kan også droppe mellomvariabelen tidl, og bare skrive

streng = "bare barn er barn, men alle er vi barnebarn"
antall = {} # Tom dictionary
for tegn in streng:
    # Øk antall forekomster med 1
    antall.update({tegn: antall.get(tegn, 0) + 1})
print(antall)

Skritt 5

Nå gjenstår det bare å sortere forekomstene alfabetisk. Det gjør vi med Python-funksjonen sorted(). For å få med verdiene og ikke bare nøklene, må vi imidlertid også bruke dictionary-metoden items(), slik det beskrives i artikkelen om å gjennomløpe dictionaries.

streng = "bare barn er barn, men alle er vi barnebarn"
antall = {} # Tom dictionary
for tegn in streng:
    # Øk antall forekomster med 1
    antall.update({tegn: antall.get(tegn, 0) + 1})
antall = sorted(antall.items())
print(antall)

Når vi kjører koden, får vi skrevet ut

[(' ', 8), (',', 1), ('a', 6), ('b', 5), ('e', 6), ('i', 1), ('l', 2), ('m', 1), ('n', 5), ('r', 7), ('v', 1)]

Bruken av items() gjør at dette ikke lenger er en dictionary.

Oppgave 1:

Koden under bruker funksjonen randint() fra modulen random til å simulere kast med to terninger og registrere summen av antall øyne på de to terningene.

from random import randint
 
oyne = randint(1, 6) + randint(1, 6)

Skriv Python-kode som gjør en million slike simuleringer og teller opp og skriver ut hvor ofte de forskjellige summene av øyne forekommer. Utskriften skal være sortert på sum øyne. Antallet forekomster vil variere fra kjøring til kjøring, men skal ha format som dette:

[(2, 27660), (3, 56008), (4, 83074), (5, 111486), (6, 139296), (7, 166093), (8, 138523), (9, 111072), (10, 83480), (11, 55679), (12, 27629)]

Se løsningsforslag

Kilder

    • Matthes A. (2019). Python Crash Course. no starch press

Gjennomløpe dictionaries

Vi kan enkelt gjennomløpe en dictionary med ei for-løkke på samme måte som ei liste.

Eksempel 1:

Vi oppretter dictionary hjul:

hjul = {
    "sykkel": 2,
    "moped": 2,
    "bil": 4,
    "atv": 4
    }

Så gjennomløper vi hjul og skriver ut innholdet:

for kjoretoy in hjul:
    print(kjoretoy)

I eksempel 1 får vi imidlertid bare skrevet ut nøklene i hjul. Vil vi skrive ut både nøkler og verdier, må vi bruke dictionary-metoden items(). items() returnerer par av nøkler og verdier.

Eksempel 2:

Vi oppretter dictionary hjul på samme måte som i eksempel 1.

Så gjennomløper vi hjul og skriver ut par av nøkler og verdier:

for kjoretoy, antall in hjul.items():
    print(kjoretoy, antall)

I eksempel 2 får vi skrevet ut både nøkler og verdier. Vi ser at vi her benytter metoden med samtidige tilordninger, som er beskrevet i artikkelen om samtidige tilordninger. items() returnerer to verdier som blir tilordnet henholdsvis kjoretoy og antall.

Dictionaries har ingen sort()-metode, slik som lister har, men vi kan sortere en dictionary på nøkler ved hjelp av Python-funksjonen sorted(). 

Eksempel 3:

Vi oppretter dictionary hjul på samme måte som i eksempel 1.

Så gjennomløper vi hjul og skriver ut par av nøkler og verdier, sortert på nøkler:

for kjoretoy, antall in sorted(hjul.items()):
    print(kjoretoy, antall)

Å sortere en dictionary på verdier er mer klumpete, og ikke noe vi kommer til må gå inn på.

Kilder

    • Matthes A. (2019). Python Crash Course. no starch press

Dictionaries

Dataypen dictionary

I artikkelen om lister presenterer vi datatypen lister som variabler som kan inneholde flere verdier. I ei liste er elementene nummerert fra 0 og oppover, og vi identifiserer et element ved hjelp av navnet på lista og en indeks, som er et løpenummer som starter på 0. Hvis for eksempel partall er ei liste, er partall[0] det første elementet i lista, med indeks 0.

Nå skal vi se på en annen datatype som kan inneholde flere verdier, dictionaries. Vi velger å bruke det engelske navnet fordi den norske oversettelsen, ordbøker, er misvisende.

Til forskjell fra i lister bruker vi en nøkkel, ikke en indeks, til å navigere i en dictionary. Når vi slår opp med nøkkelen, får vi ut den tilhørende verdien.

En dictionary inneholder et vilkårlig antall par av nøkler og verdier. Vi kan opprette en dictionary ved å ramse opp innholdet på samme måte som i ei liste, men vi bruker krøllparenteser, ikke klammeparenteser, og innholdet oppgis som par av nøkler og verdier med kolon mellom.

Eksempel 1:

Vi oppretter dictionary antall_hjul:

antall_hjul = {"sykkel": 2, "moped": 2, "bil": 4, "ATV": 4}

Her er nøklene navnene på forskjellige typer kjøretøy, og verdiene er antall hjul kjøretøyene har.

Stilguiden PEP 8 anbefaler mellomrom etter, men ikke foran, kolon i dictionaries.

Har vi mer enn noen få nøkkel-verdi-par, blir gjerne en dictionary for lang til få plass på ei linje, da plasserer vi parene under hverandre:

antall_hjul = {
    "sykkel": 2,
    "moped": 2,
    "bil": 4,
    "ATV": 4
    }

Innrykkene er ikke noe krav i Python, men anbefales i PEP 8.

Vi kan skrive ut alle parene i en dictionary med print() på samme måte som i ei liste. For å se hvordan antall_hjul fra eksempel 1 ser ut, skriver vi

print(antall_hjul)

For å slå opp i en dictionary kan vi bruke dictionary-navnet etterfulgt av nøkkelen i klammeparentes. Her bruker vi altså samme type parentes som i lister, ikke krøllparentes.

Basert på antall_hjul i eksempel 1 vil

print(antall_hjul["sykkel"])

skrive ut 2.

I eksempel 1 var nøklene strenger og verdiene tall. Men nøklene kan også godt være tall.

Eksempel 2:

Vi oppretter dictionary siffer_norsk:

siffer_norsk = {
    1: "en",
    2: "to",
    3: "tre",
    4: "fire",
    5: "fem",
    6: "seks",
    7: "syv",
    8: "åtte",
    }

Her er nøklene sifrene fra 1 til 8, og verdiene er sifrenes navn på norsk.

Skriver vi for eksempel

print(siffer_norsk[3])

får vi skrevet ut tre.

På samme måte som ved lister kan vi bruke kodeordet in til å avgjøre om en nøkkel finnes i en dictionary. Hvis dict er en dictionary og k en nøkkel, vil uttrykket k in dict være True hvis k finnes i dict, False hvis ikke. 

Basert på siffer_norsk i eksempel 2, vil for eksempel 

print(5 in siffer_norsk)
print(9 in siffer_norsk)

skrive ut henholdsvis True og False, fordi 5 er en nøkkel i siffer_norsk, men ikke 9.

Vil vi sjekke om en verdi ligger i en dictionary, må vi involvere dictionary-metoden values(). Hvis dict er en dictionary og v en verdi, vil uttrykket v in dict.values() være True hvis v finnes i dict, False hvis ikke.

Basert på siffer_norsk i eksempel 2, vil for eksempel 

print("fem" in siffer_norsk.values())
print("ni" in siffer_norsk.values())

skrive ut henholdsvis True og False, fordi "fem" er en verdi i siffer_norsk, men ikke "ni".

Dictionary-metoder

På samme måte som lister, har også dictionaries metoder knyttet til seg. En oversikt finnes her: https://www.w3schools.com/python/python_ref_dictionary.asp

Vi skal presentere noen av dem.

update()

Denne metoden kan brukes til å endre eller legge til innhold i en dictionary. Som argument til update() gir vi ett eller flere par med nøkler og verdier mellom krøllparenteser. Hvis en nøkkel finnes i dictionary fra før, blir den tilhørende verdien oppdatert. Hvis ikke, blir et nytt nøkkel-verdi-par lagt til.

Eksempel 3:

Vi har dictionary siffer_norsk fra eksempel 2, og skriver

siffer_norsk.update({7: "sju"})
siffer_norsk.update({0: "null"})

Nøkkelen 7 finnes fra før, så verdien knyttet til 7 blir endret fra "syv" til "sju".

Nøkkelen 0 finnes ikke, så paret 0: "null" blir lagt til.

Vi kan oppnå det samme uten update() ved å bruke nøklene direkte:

siffer_norsk[7] = "sju"
siffer_norsk[0] = "null"

Det blir smak og behag hva vi velger. Med update() kan vi imidlertid angi flere par samtidig, og utføre det vi gjorde i to operasjoner over i én enkelt operasjon:

siffer_norsk.update({7: "sju", 0: "null"})

Nye par legges fortløpende til på slutten. Så i eksempel 3 vil 0 bli lagt til etter 8.

pop()

Fjerner et par med en nøkkel vi gir inn som argument.

Eksempel 4:

Vi har dictionary siffer_norsk fra eksempel 2 og skriver

siffer_norsk.pop(5)

Da blir paret 5: "fem" fjernet fra siffer_norsk.

Hvis nøkkelen ikke finnes i dictionary, genererer Python feilkoden KeyError.

På samme måte som i lister, returnerer pop() verdien den fjerner, og denne verdien kan vi tilordne til en variabel. Skriver vi for eksempel poppet = siffer_norsk.pop(5), får variabelen poppet verdien «fem».

popitem()

Fjerner siste nøkkel-verdi-par som ble lagt inn i dictionary.

get()

Slår opp med en nøkkel vi gir inn som et argument, og returnerer den tilhørende verdien. I motsetning til pop() og popitem() fjerner imidlertid ikke get() noe fra dictionary.

Eksempel 5:

Vi har dictionary siffer_norsk fra eksempel 2 og skriver

print(siffer_norsk.get(4))

Da får vi skrevet ut fire.

Dette er det samme som å slå opp direkte med 4 som nøkkel:

print(siffer_norsk[4])

I eksempel 5 så vi at vi at vi kan slå opp i en dictionary både ved å bruke metoden get() og ved å skrive navnet på dictionary etterfulgt av nøkkelen i klammeparentes. Den siste metoden vil imidlertid gi feilkoden KeyError hvis nøkkelen ikke finnes i dictionary, mens get() vil returnere None. Det er også mulig å gi inn et argument til get() som skal returneres hvis nøkkelen ikke finnes.

Eksempel 6:

Vi har dictionary siffer_norsk fra eksempel 2 og skriver

print(siffer_norsk[9])

Da får vi en feilmelding, og Python genererer KeyError, fordi 9 ikke er en nøkkel i siffer_norsk.

Skriver vi i stedet

print(siffer_norsk.get(9))

Får vi skrevet ut None.

Skriver vi

print(siffer_norsk.get(9, "Ukjent nøkkel"))

Får vi skrevet ut Ukjent nøkkel.

clear()

Tømmer en dictionary, det vil si at alt innhold fjernes.

Oppgave 1:

Skriv kode som

        • Oppretter en dictionary som heter landsprefiks, og inneholder følgende nøkkel-verdi-par: "Afghanistan": 93, "Albania": 355, "Algerie": 213, "Andorra": 0, "Angola": 244.
        • Endrer verdien tilknyttet nøkkelen "Andorra" til 376 og legger til paret "Anguilla": 1264.
        • Slår opp og skriver ut verdien knyttet til nøkkelen "Angola".
        • Slår opp og skriver ut meldingen "Ukjent land" ved oppslag med nøkkelen "Argentina".

Se løsningsforslag

Dictionaries har ikke orden på samme måte som lister. I motsetning til i lister spiller rekkefølgen på innholdet ingen rolle. Mens for eksempel lista [1, 2] vil være forskjellig fra [2, 1], der innholdet er byttet om, vil dictionary {1: "a", 2: "b"} være lik {2: "b", 1: "a"}, selv om innholdet er byttet om. Det finnes heller ingen metoder knyttet til bestemte posisjoner i dictionaries, slik som insert(), append() og pop() uten argument, slik vi har i lister.

På den annen side har dictionaries orden i den forstand at innholdet blir liggende i den rekkefølgen det blir lagt inn. Så hvis vi gjennomløper en dictionary med ei for-løkke, vil vi alltid få innholdet ut i samme rekkefølge som vi la det inn. Rekkefølgen forandres heller ikke hvis vi endrer en verdi med update().

Vi velger derfor å si at dictionaries har delvis orden.

Kilder

    • Matthes A. (2019). Python Crash Course. no starch press

I språk som JavaScript og C++ kalles dictionaries associative arrays, eller assosiative tabeller på norsk.