Sanakirja
Tarkastellaan esimerkkiä, jossa haluamme toteuttaa puhelinluettelon. Luettelo koostuu pareista, jotka sisältävät henkilön nimen ja hänen puhelinnumeronsa. Haluamme, että rakenteesta pystyy helposti etsimään halutun henkilön puhelinnumeron, siihen pystyy lisäämään uusia puhelinnumeroita ja poistamaan vanhentuneita puhelinnumeroita. Yksinkertaisuuden vuoksi oletamme, että luettelossamme ei ole useita samannimisiä henkilöitä ja että jokaisella henkilöllä on vain yksi puhelinnumero.
Kuvattu rakenne olisi mahdollista toteuttaa listojen avulla. Kunkin henkilön nimi ja puhelinnumero muodostaisi yhden listan, ja itse puhelinluettelo olisi lista, jonka alkiot olisivat nimi–puhelinnumero-listoja. Tämän rakenteen ongelma on kuitenkin se, että halutun henkilön puhelinnumeron haku siitä olisi hidasta. Jos haluaisimme etsiä Matti Meikäläisen puhelinnumeron, ohjelman pitäisi käydä koko puhelinluettelolistaa läpi niin kauan, että se löytäisi nimen Matti Meikäläinen. Pahimmillaan koko puhelinluettelo pitäisi käydä läpi. Jos nimet olisi tallennettu puhelinluetteloon aakkosjärjestyksessä, hakua voitaisiin nopeuttaa selvästi käyttämällä binäärihakua, mutta tällöin taas uusien nimien lisääminen puhelinluetteloon aakkkosjärjestyksen mukaisille paikoilleen olisi hidasta (kaikkia lisättävän nimen jälkeen tulevia alkioita pitäisi siirtää listassa loppuun päin).
Python tarjoaa toisen rakenteen, jossa on mahdollista toteuttaa tehokkaasti alkion haku avaimen perusteella (puhelinluetteloesimerkissä henkilön nimi toimii avaimena) sekä uusien alkioiden haku ja vanhojen poisto. Tätä rakennetta kutsutaan sanakirjaksi (engl. dictionary). Sanakirjan tehokas toteuttaminen perustuu hajautusrakenteeseen, jota käsitellään tarkemmin Tietorakenteet ja algoritmit -kurssilla.
Tyhjän sanakirjan voi luoda aaltosulkujen {}
avulla, esimerkiksi
puh_luettelo = {}
Sanakirjaa luodessa voi myös antaa samalla jo sanakirjaan lisättäviä avain–arvo-pareja. (Puhelinluetteloesimerkissä avain on nimi ja arvo nimeen liittyvä puhelinnumero). Avain ja arvo erotetaan toisistaan kaksoispisteellä ja eri parit toisistaan pilkulla:
puhelinluettelo = {"Teekkari Teemu" : "050-12345", "Fyysikko Tiina" : \
"045-234567", "Kemisti Kalle" : "040-765432"}
luo uuden sanakirjan, joka sisältää kolme nimi–puhelinnumero-paria.
Johonkin avaimeen liittyvän arvon saa selville ilmauksella
sanakirja[avain]
, esimerkiksi
print(puhelinluettelo["Fyysikko Tiina"])
tulostaa
045-234567
Sanakirjan on kuitenkin tällöin sisällettävä hakasulkujen sisällä annettu avain. Muuten aiheutuu virhetilanne, esimerkiksi käskyn
print(puhelinluettelo["Virtanen Maija"])
suorittaminen edellä luodulle sanakirjalle saa aikaan ohjelman kaatumisen ja tulostuksen
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'Virtanen Maija'
Operaattorin in
avulla voidaan kuitenkin tutkia ensin, onko avain
sanakirjassa. Jos muuutuja puhelinluettelo
viittaa edellä luotuun
sanakirjaan, niin rivien
nimi = "Virtanen Maija"
if nimi in puhelinluettelo:
print(puhelinluettelo[nimi])
else:
print("Nimea ei loydy luettelosta")
suoritus saa aikaan tulostuksen
Nimea ei loydy luettelosta
Rivien
nimi = "Teekkari Teemu"
if nimi in puhelinluettelo:
print(puhelinluettelo[nimi])
else:
print("Nimea ei loydy luettelosta")
Suoritus puolestaan aiheuttaa tulostuksen
050-12345
Sanakirjaan voidaan lisätä sijoituskäskyn avulla uusia avain–arvo-pareja
sekä muuttaa aikaisemmin lisättyihin avaimiin liittyviä arvoja. Jos esimerkiksi
suoritetaan seuraavat käskyt, kun muuttuja puhelinluettelo
viittaa edellä luotuun
sanakirjaan, rivien
puhelinluettelo["Rakentaja Niina"] = "0400-123"
puhelinluettelo["Kemisti Kalle"] = "041-56789"
print(puhelinluettelo)
Tulostus on
{'Teekkari Teemu': '050-12345', 'Fyysikko Tiina': '045-234567', 'Kemisti Kalle':
'041-56789', 'Rakentaja Niina': '0400-123'}
Kaikki sanakirjaan kuuluvat avaimet voi käydä läpi for-käskyn avulla samaan tapaan kuin listan alkiot:
for nimi in puhelinluettelo:
print(nimi)
tulostaa
Teekkari Teemu
Fyysikko Tiina
Kemisti Kalle
Rakentaja Niina
ja
for nimi in puhelinluettelo:
print(f"{nimi:16s} {puhelinluettelo[nimi]:12s}")
tulostaa
Teekkari Teemu 050-12345
Fyysikko Tiina 045-234567
Kemisti Kalle 041-56789
Rakentaja Niina 0400-123
Sanakirjasta voi poistaa jonkin avaimen ja siihen liittyvän arvon
del
-operaattorilla:
del puhelinluettelo["Kemisti Kalle"]
print(puhelinluettelo)
Tulostaa
{'Teekkari Teemu': '050-12345', 'Fyysikko Tiina': '045-234567',
'Rakentaja Niina': '0400-123'}
Pythonin versiosta 3.7 lähtien sanakirjoissa säilyy se järjestys, jossa avaimet on lisätty sanakirjaan. Tätä vanhemmissa Python-versioissa avain-arvo-pareja säilytetään sanakirjassa satunnaisessa järjestyksessä. Kun esimerkiksi käydään läpi toistokäskyssä kaikki sanakirjan avaimet yllä esitettyjen esimerkkien mukaisesti, avaimet ja niihin liittyvät arvot voidaan tulostaa täysin eri järjestyksessä kuin mitä ne on alunperin lisätty sanakirjaan. Jos on mahdollisuus siihen, että ohjelmaa ajetaan Pythonin vanhemmilla versioilla, pitää ohjelma kirjoittaa niin, että siinä ei missään vaiheessa oleteta avainten olevan sanakirjassa jossain määrätyssä järjestyksessä.
Lopuksi esitetään esimerkkinä sanakirjoista yksinkertainen puhelinluettelo-ohjelma, joka lukee ensin käyttäjältä joukon nimiä ja puhelinnumeroita. Se tallentaa ne sanakirjarakenteeseen. Tämän jälkeen ohjelma esittää käyttäjälle valikon, josta käyttäjä voi valita haluamansa toimenpiteen (puhelinnumeron etsimisen, puhelinnumeron muuttamisen tai ohjelman lopettamisen). Ohjelma suorittaa käyttäjän valitseman toimenpiteen ja esittää valikon sen jälkeen käyttäjälle aina uudelleen niin kauan, että käyttäjä valitse ohjelman lopettamisen.
Tällaisessa puhelinluettelo-ohjelmassa ei ole vielä paljon järkeä, koska käyttäjä joutuu itse antamaan kaikki luetteloon lisättävät nimet ja numerot ohjelman jokaisella suorituskerralla. Ohjelmaa on kuitenkin helppo muuttaa niin, että nimet ja puhelinnumerot luetaankin tiedostosta.
Ohjelma on alla useassa osassa, jotta olisi mahdollisuus nähdä helpommin koodirivi ja sitä koskeva selitys yhtä aikaa.
# Funktio lukee kayttajalta nimia ja puhelinnumeroita.
# Se luo uuden sanakirjarakenteen ja tallentaa siihen
# luetut nimet ja puhelinnumerot. Funktio palauttaa
# taman sanakirjarakenteen.
def lue_puhelinnumerot():
print("Anna lisattavat nimet ja numerot.")
print("Nimi ja puhelinnumero samalla rivilla,")
print("valissa kaksoispiste.")
print("Lopeta tyhjalla rivilla.")
puhelinluettelo = {}
rivi = input()
while len(rivi) > 0:
tiedot = rivi.split(":")
nimi = tiedot[0]
numero = tiedot[1]
puhelinluettelo[nimi] = numero
rivi = input()
return puhelinluettelo
# Funktio saa parametrina puhelinluettelon sisaltavan
# sanakirjarakenteen. Se pyytaa kayttajalta yhden nimen
# ja etsii puhelinluettelosta sita vastaavan puhelinnumeron.
def etsi_numero(puhelintiedot):
etsitty = input("Kenen numero haetaan? ")
if etsitty in puhelintiedot:
print("Numero on", puhelintiedot[etsitty])
else:
print("Nimea ei loydy luettelosta.")
etsitty
) kuuluu sanakirjan avaimiin,
siihen liittyvä puhelinnumero saadaan näin.# Funktio saa parametrina puhelinluettelon sisaltavan
# sanakirjarakenteen. Se pyytaa kayttajalta yhden nimen
# ja vaihtaa talle nimelle kayttajan antaman uuden
# puhelinnumeron.
def muuta_numero(numerotiedot):
muutettava = input("Kenen numeron haluat muuttaa? ")
if muutettava not in numerotiedot:
print("Nimea ei loydy luettelosta.")
else:
uusi_numero = input("Anna taman henkilon uusi numero. ")
numerotiedot[muutettava] = uusi_numero
# Funktio tulostaa kayttajalle mahdolliset toiminnot
# ja lukee ja palauttaa hanen valitsemansa toiminnon
# numeron.
def pyyda_valinta():
print()
print("Valitse toiminto:")
print("1. Hae puhelinnumero")
print("2. Muuta puhelinnumero")
print("3. Lopeta")
valinta = int(input())
return valinta
def main():
luettelo = lue_puhelinnumerot()
toiminto = pyyda_valinta()
while toiminto != 3:
if toiminto == 1:
etsi_numero(luettelo)
elif toiminto == 2:
muuta_numero(luettelo)
else:
print("Vaara valinta!")
toiminto = pyyda_valinta()
print("Ohjelman suoritus paattyy.")
main()