Osa 5

Sanakirja

Lista on kätevä tietorakenne, mutta sen rajoituksena on, että alkiot ovat indekseissä 0, 1, 2, jne. Tämä hankaloittaa alkioiden etsimistä listalta: jotta löydämme tietyn alkion, on pahimmassa tapauksessa käytävä läpi koko lista.

Tutustumme seuraavaksi sanakirjaan, (englanniksi dictionary) joka on listan lisäksi toinen Pythonin perustietorakenne. Sanakirjassa jokainen alkio koostuu avaimesta ja arvosta, ja voimme etsiä ja muuttaa tietoa avaimen perusteella.

Sanakirjan käyttäminen

Seuraava ohjelma näyttää esimerkin sanakirjan käyttämisestä:

sanakirja = {}

sanakirja["apina"] = "monkey"
sanakirja["banaani"] = "banana"
sanakirja["cembalo"] = "harpsichord"

print(len(sanakirja))
print(sanakirja)
print(sanakirja["apina"])
Esimerkkitulostus

3 {'apina': 'monkey', 'banaani': 'banana', 'cembalo': 'harpsichord'} monkey

Merkintä {} luo tyhjän sanakirjan, minkä jälkeen voimme lisätä sanakirjaan sisältöä. Tässä tapauksessa lisäämme kolme avainta "apina", "banaani" ja "cembalo", joita vastaavat arvot "monkey", "banana" ja "harpsichord". Lopuksi tulostamme koko sanakirjan sisällön ja sitten avaimen "apina" arvon.

Voisimme käyttää tätä sanakirjaa vaikka seuraavasti:

sana = input("Anna sana: ")
if sana in sanakirja:
    print("Käännös:", sanakirja[sana])
else:
    print("Sanaa ei löytynyt")

Tässä käytössä on in-operaattori, joka sanakirjan tapauksessa tarkastaa, onko siinä tiettyä avainta. Mahdollisia ohjelman tulostuksia:

Esimerkkitulostus

Anna sana: apina Käännös: monkey

Esimerkkitulostus

Anna sana: pöllö Sanaa ei löytynyt

Mitä sanakirjassa voi olla?

Vaikka tietorakenteen nimi on sanakirja, siinä ei ole usein sanakirjaa vaan jotain muuta tietoa. Esimerkiksi seuraavassa sanakirjassa avaimet ovat merkkijonoja ja arvot ovat kokonaislukuja:

tulokset = {}
tulokset["Maija"] = 4
tulokset["Liisa"] = 5
tulokset["Kalle"] = 2

Seuraavassa sanakirjassa puolestaan avaimet ovat kokonaislukuja ja arvot ovat listoja:

listat = {}
listat[5] = [1, 2, 3]
listat[42] = [5, 4, 5, 4, 5]
listat[100] = [5, 2, 3]

Avaimista ja arvoista

Tietty avain voi esiintyä sanakirjassa enintään kerran. Jos asetamme samalle avaimelle uuden arvon, korvaa uusi arvo vanhan arvon:

sanakirja["suuri"] = "big"
sanakirja["suuri"] = "large"
print(sanakirja["suuri"])
Esimerkkitulostus

large

Sanakirjan avaimen vaatimuksena on, että sen tulee olla muuttumaton. Tämän vuoksi emme voi käyttää listaa avaimena, koska lista voi muuttua. Esimerkiksi seuraava koodi ei toimi:

sanakirja[[1, 2, 3]] = 5
Esimerkkitulostus

TypeError: unhashable type: 'list'

Huomaa, että sanakirjassa olevaa avainta vastaavan arvon ei tarvitse olla muuttumaton, vaan voimme tallentaa mitä tahansa tietoa arvoiksi. Sama arvo voi myös esiintyä samassa hakemistossa enemmän kuin yhden kerran.

Loading
Loading

Sanakirjan läpikäynti

Sanakirjan läpikäyntiin voidaan käyttää tuttuun tapaan for-silmukkaa. Rakenne for avain in sanakirja käy läpi kaikki sanakirjan avaimet yksi kerrallaan. Esimerkiksi seuraava koodi tulostaa kaikki sanakirjan avaimet ja niiden arvot:

sanakirja = {}

sanakirja["apina"] = "monkey"
sanakirja["banaani"] = "banana"
sanakirja["cembalo"] = "harpsichord"

for avain in sanakirja:
    print("avain:", avain)
    print("arvo:", sanakirja[avain])
Esimerkkitulostus

avain: apina arvo: monkey avain: banaani arvo: banana avain: cembalo arvo: harpsichord

Python tarjoaa myös mahdollisuuden käydä läpi samaan aikaan sekä avaimet että vastaavat arvot. Tämä onnistuu käyttämällä items-metodia, joka palauttaa kaikki avaimet ja arvot yksi kerrallaan:


for avain, arvo in sanakirja.items():
    print("avain:", avain)
    print("arvo:", arvo)

Huomaa, että läpikäynnissä avaimet tulevat samassa järjestyksessä kuin ne on lisätty sanakirjaan. Sanakirjan avainten järjestyksellä ei kuitenkaan yleensä ole merkitystä sovelluksissa.

Sanakirjan edistyneempi käyttö

Tarkastellaan tilannetta, jossa listassa on joukko sanoja:

sanalista = [
  "banaani", "maito", "olut", "juusto", "piimä", "mehu", "makkara",
  "tomaatti", "kurkku", "voi", "margariini", "juusto", "makkara",
  "olut", "piimä", "piimä", "voi", "olut", "suklaa"
]

Haluamme analysoida sanalistaa eri tavoin, kuten selvittää, montako kertaa kukin sana listalla esiintyy.

Sanakirja sopii tähän tilanteeseen hyvin. Ideana on käydä listan sanat läpi yksi kerrallaan ja ylläpitää sanakirjassa tietoa sanojen esiintymiskerroista:

def lukumaarat(lista):
    sanat = {}
    for sana in lista:
        # jos sana ei ole vielä tullut vastaan, alusta avaimen arvo
        if sana not in sanat:
            sanat[sana] = 0
        # kasvata sanan esiintymislukumäärää
        sanat[sana] += 1
    return sanat

# kutsutaan funktiota
print(lukumaarat(sanalista))

Ohjelman tulostus on seuraavassa:

Esimerkkitulostus

{'banaani': 1, 'maito': 1, 'olut': 3, 'juusto': 2, 'piimä': 3, 'mehu': 1, 'makkara': 2, 'tomaatti': 1, 'kurkku': 1, 'voi': 2, 'margariini': 1, 'suklaa': 1}

Tehdään vielä toinen sanalistaa käsittelevä metodi, joka jaottelee listalla olevat sanat niiden alkukirjaimen mukaan:

def alkukirjaimen_mukaan(lista):
    ryhmat = {}
    for sana in lista:
        alkukirjain = sana[0]
        # alusta alkukirjaimeen liittyvä lista kun kirjain tulee vastaan 1. kerran
        if alkukirjain not in ryhmat:
            ryhmat[alkukirjain] = []
        # lisää sana alkukirjainta vastaavalle listalle
        ryhmat[alkukirjain].append(sana)
    return ryhmat

ryhmat = alkukirjaimen_mukaan(sanalista)

for avain, arvo in ryhmat.items():
    print(f"kirjaimella {avain} alkavat sanat: ")
    for sana in arvo:
        print(sana)

Funktio toimii pitkälti saman periaatteen mukaan kuin edellisen esimerkin funktio. Tällä kertaa kuitenkin sanakirjassa avaimiin (eli alkukirjaimiin) liittyvät arvot ovat listoja.

Ohjelman tulostus on seuraavassa:

Esimerkkitulostus

kirjaimella b alkavat sanat: banaani kirjaimella m alkavat sanat: maito mehu makkara margariini makkara kirjaimella o alkavat sanat: olut olut olut kirjaimella j alkavat sanat: juusto juusto kirjaimella p alkavat sanat: piimä piimä piimä kirjaimella t alkavat sanat: tomaatti kirjaimella k alkavat sanat: kurkku kirjaimella v alkavat sanat: voi voi kirjaimella s alkavat sanat: suklaa

Loading
Loading
Loading

Avaimien poistaminen sanakirjasta

Sanakirjasta on mahdollista myös poistaa avain-arvo-pareja. Menetelmiä tähän on kaksi. Ensimmäinen näistä on komento del:

henkilokunta = {"Antti": "lehtori", "Emilia": "professori", "Arto": "Lehtori"}
del henkilokunta["Arto"]
print(henkilokunta)
Esimerkkitulostus

{'Antti': 'lehtori', 'Emilia': 'professori'}

Jos komentoa del kutsutaan avaimille, joita sanakirjassa ei ole, seurauksena on virhe:

henkilokunta = {"Antti": "lehtori", "Emilia": "professori", "Arto": "lehtori"}
del henkilokunta["Jukka"]
Esimerkkitulostus
>>> del henkilokunta["Jukka"]
Traceback (most recent call last):
  File "", line 1, in 
KeyError: 'Jukka'

Ennen poistoa on siis syytä tarkistaa, että poistettava avain löytyy sanakirjasta:

henkilokunta = {"Antti": "lehtori", "Emilia": "professori", "Arto": "lehtori"}
if "Jukka" in henkilokunta:
  del henkilokunta["Jukka"]
  print("Poistettiin")
else:
  print("Poistettavaa henkilöä ei löytynyt henkilökunnasta")

Toinen vaihtoehto alkion poistamiseen on metodi pop:

henkilokunta = {"Antti": "lehtori", "Emilia": "professori", "Arto": "lehtori"}
poistettu = henkilokunta.pop("Arto")
print(henkilokunta)
print("Poistettiin", poistettu)
Esimerkkitulostus

{'Antti': 'lehtori', 'Emilia': 'professori'} Poistettiin lehtori

Metodi pop siis myös palauttaa poistettua avainta vastaavan arvon.

Oletusarvoisesti myös pop aiheuttaa virheen, jos sanakirjasta yritetään poistaa avain, jota siellä ei ole. Metodille on kuitenkin mahdollista antaa toisena parametrina oletusarvoinen paluuarvo, joka palautetaan siinä tilanteessa, kun poistettavaa ei löydy. Esimerkiksi arvo None, joka tarkoittaa "ei mitään", sopii hyvin tälläisiin tilanteisiin:

henkilokunta = {"Antti": "lehtori", "Emilia": "professori", "Arto": "lehtori"}
poistettu = henkilokunta.pop("Jukka", None)
if poistettu == None:
  print("Poistettavaa henkilöä ei löytynyt henkilökunnasta")
else:
  print("Poistettiin", poistettu)
Esimerkkitulostus

Poistettavaa henkilöä ei löytynyt henkilökunnasta

Kannattaa huomata, että jos on tarvetta poistaa koko sanakirjan sisältö:

henkilokunta = {"Antti": "lehtori", "Emilia": "professori", "Arto": "lehtori"}
for avain in henkilokunta:
  del henkilokunta[avain]

seurauksena on virheilmoitus

Esimerkkitulostus

RuntimeError: dictionary changed size during iteration

Syynä on se, että käytäessä läpi rakennetta for-lauseella, ei sen sisältöä saa muuttaa.

Koko sanakirjan tyhjennys onnistuu komennolla:

henkilokunta.clear()
Loading
Loading

Sanakirja tiedon ryhmittelyssä

Voimme käyttää sanakirjaa myös tiedon ryhmittelyssä. Esimerkiksi seuraava koodi luo sanakirjan, jossa on tietoa henkilöstä:

henkilo = {"nimi": "Pirjo Python", "pituus": 154, "paino": 61, "ikä:" 44}

Tämä tarkoittaa, että henkilön nimi on Pirjo Python, pituus on 154, paino on 61 ja ikä on 44. Huomaa, että olisimme voineet tallentaa tiedot myös näin muuttujiin:

nimi = "Pirjo Python"
pituus = 154
paino = 61
ika = 44

Sanakirjan etuna on kuitenkin, että se kokoaa kaikki samaan asiaan liittyvät tiedot yhteisen nimen alle, jonka kautta voimme viitata tietoihin. Periaatteessa lista tarjoaa saman edun:

henkilo = ["Pirjo Python", 153, 61, 44]

Listan huono puoli on kuitenkin, että ohjelmoijan on muistettava, mihin kohtaan listaa mikäkin arvo tallennetaan. Pitää siis muistaa esimerkiksi, että henkilo[2] tarkoittaa painoa ja henkilo[3] ikää. Sanakirjassa tätä ongelmaa ei ole, sillä kaikki sanakirjassa olevat erilliset tiedot on tallennettu selkeästi nimetyn avaimen taakse.

Esimerkiksi voimme käsitellä henkilöitä näin:

henkilo1 = {"nimi": "Pirjo Python", "pituus": 154, "paino": 61, "ikä": 44}
henkilo2 = {"nimi": "Pekka Pythonen", "pituus": 174, "paino": 103, "ikä": 31}
henkilo3 = {"nimi": "Pedro Python", "pituus": 191, "paino": 71, "ikä": 14}

henkilot = [henkilo1, henkilo2, henkilo3]

for henkilo in henkilot:
    print(henkilo["nimi"])

yhteispituus = 0
for henkilo in henkilot:
    yhteispituus += henkilo["pituus"]

print("Keskipituus on", yhteispituus / len(henkilot))
Esimerkkitulostus

Pirjo Python Pekka Pythonen Pedro Python Keskipituus on 173.0

Loading
Loading
Loading...
:
Loading...

Log in to view the quiz

Seuraava osa: