Termipankki
  1. A
    1. Absoluuttinen polku
    2. Ajonaikainen
      konseptit
    3. Alkio
      arvot listat
    4. Alustaminen
      muuttujat arvot
    5. Argumentti
      arvot funktiot
    6. Arvo
      arvot
    7. Avain
      sanakirjat arvot
    8. Avainsana
      nimet
    9. Avainsana-argumentti
      funktiot
    10. Avausmoodi
      tiedostot
    11. Aliohjelma
      Funktio
    12. Attribuutti
      Jäsenarvo
    13. Ajaminen
      Suorittaminen
  2. B
    1. Boolen operaattori
      Looginen operaattori
    2. Bugi
      Ohjelmointivirhe
    3. break
      toistorakenteet avainsanat
  3. C
    1. Carriage return
      pakeneminen merkkijonot tiedostot windows
    2. Ctrl + C
      Näppäimistökeskeytys
    3. Callback
      Takaisinkutsu
    4. continue
      toistorakenteet avainsanat
  4. D
    1. Data
    2. Debuggaus
    3. Dokumenttimerkkijono
      dokumentointi
  5. E
    1. Elementti
      Alkio
    2. Ehto
      ohjausrakenteet
    3. Ehtolause
      ohjausrakenteet
    4. Ehtorakenne
      ehtorakenteet ohjausrakenteet
    5. Epätosi
      arvot
    6. Erotin
      merkkijonot tiedostot listat syöte
    7. Evaluointi
      lausekkeet arvot
    8. Exception-luokka
      poikkeukset ohjausrakenteet
    9. enumerate
      listat toistorakenteet
  6. F
    1. False
      Epätosi
    2. Format
      merkkijonot tulostus
    3. Funktio
      funktiot
    4. Funktiokutsu
      funktiot lauseet
    5. Funktiomäärittely
    6. f-merkkijono
      merkkijonot
    7. for
  7. G
    1. Generaattori
      objektit toistorakenteet
    2. Globaali muuttuja
      muuttujat arvot
    3. Globaali näkyvyys
  8. H
    1. Haara
      try-rakenteet ehtorakenteet
    2. Hyppy
      ohjausrakenteet
    3. Hardkoodaus
      Kovakoodaus
  9. I
    1. if-lause
      Ehtolause
    2. if-rakenne
      Ehtorakenne
    3. Ikuinen silmukka
      toistorakenteet
    4. Indeksi
      arvot listat
    5. Indeksiosoitus
      arvot listat
    6. import
      moduulit
  10. J
    1. Jäsenarvo
      objektit
    2. Jäsenfunktio
      Metodi
  11. K
    1. Kutsu
      Funktiokutsu
    2. Kierros
      toistorakenteet
    3. Kirjasto
      moduulit
    4. Komentoriviargumentti
      terminaali
    5. Kommentti
      virheenetsintä dokumentointi
    6. Kooditiedosto
      konseptit
    7. Kovakoodaus
      arvot
    8. Kutsupyyntö
      funktiot
    9. Käsittelijä
      funktiot konseptit
    10. Käyttöliittymä
      konseptit
    11. Käyttöliittymäelementti
    12. Koodilohko
      Lohko
    13. Koodi
      Lähdekoodi
    14. KeyboardInterrupt
      Näppäimistökeskeytys
    15. Komentorivi
      Terminaali
    16. Komentokehote
      Terminaali
    17. Kahva
      Tiedostokahva
  12. L
    1. Lause
      konseptit
    2. Lauseke
      konseptit
    3. Leikkaus
      listat
    4. Lista
    5. Literaaliarvo
      arvot
    6. Liukuluku
      arvot tyypit
    7. Lohko
      ohjausrakenteet funktiot
    8. Looginen operaattori
      ohjausrakenteet operaattorit
    9. Lähdekoodi
      konseptit
  13. M
    1. Muotoilu
      Format
    2. Merkki
    3. Merkkijono
      arvot tyypit
    4. Metodi
      funktiot objektit
    5. Metodikutsu
      lausekkeet objektit
    6. Moduuli
    7. Monikko
      tietorakenteet listat
    8. Muuntumaton
      arvot merkkijonot konseptit
    9. Muuntuva
      arvot konseptit listat
    10. Muuttuja
      arvot konseptit
    11. Määrittely
      konseptit
  14. N
    1. Nimeämätön vakio
      arvot vakiot
    2. Nimi
      muuttujat funktiot
    3. Nimiavaruus
      moduulit funktiot konseptit
    4. Nimikonflikti
    5. Näkyvyysalue
      konseptit lohkot
    6. Näppäimistökeskeytys
      poikkeukset
  15. O
    1. Objekti
      konseptit
    2. Olio
      Objekti
    3. Ohjausrakenne
      try-rakenteet toistorakenteet ehtorakenteet
    4. Ohjelmointiongelma
      ongelmanratkaisu
    5. Ohjelmointityyli
    6. Ohjelmointivirhe
      ongelmanratkaisu
    7. Oletusarvo
      arvot funktiot parametrit
    8. Ominaisuus
      objektit
    9. Operaatio
      lausekkeet
    10. Operaattori
    11. Operandi
  16. P
    1. Paikallinen muuttuja
    2. Paikanpidin
      merkkijonot tulostus
    3. Pakeneminen
      merkkijonot
    4. Palauttaminen
      arvot funktiot
    5. Paluuarvo
    6. Parametri
      funktiot
    7. Parametrisaatio
    8. Poikkeus
      try-rakenteet ongelmanratkaisu
    9. Poikkeusten käsittely
      ohjausrakenteet poikkeukset
    10. Polku
    11. Python-konsoli
      työkalut
    12. Python-tulkki
      työkalut
    13. Pääohjelma
      konseptit
    14. Presedenssi
      Sidontajärjestys
  17. R
    1. Rajapinta
      moduulit funktiot konseptit
    2. Ratkaisumalli
      ongelmanratkaisu
    3. Rekursio
      funktiot konseptit
    4. Relatiivinen polku
    5. Rivinvaihtomerkki
      merkkijonot tiedostot
  18. S
    1. Sanakirja
      tietorakenteet
    2. Sapluuna
      merkkijonot konseptit
    3. Sekvenssi
      tietorakenteet konseptit toistorakenteet
    4. Sidontajärjestys
      lausekkeet konseptit
    5. Suoritusjärjestys
      Sidontajärjestys
    6. Sijoittaminen
      muuttujat arvot
    7. Sijoitusoperaattori
      muuttujat arvot operaattorit
    8. Silmukkamuuttuja
      muuttujat toistorakenteet
    9. Sisennys
      konseptit
    10. Sisäänrakennettu funktio
      funktiot
    11. Suorittaminen
      lausekkeet konseptit
    12. Suoritusjärjestys
    13. Syntaksi
      konseptit
    14. Syntaksivirhe
      poikkeukset
    15. Syöte
      merkkijonot konseptit
    16. Silmukka
      Toistorakenne
    17. Stacktrace
      Traceback
  19. T
    1. Taikaluku
      Nimeämätön vakio
    2. try-rakenne
      Poikkeusten käsittely
    3. Takaisinkutsu
      funktiot
    4. Tallennusformaatti
      merkkijonot tiedostot
    5. Tapahtuma
      konseptit
    6. Tekstitiedosto
      tiedostot
    7. Terminaali
      työkalut
    8. Testaaminen
      ongelmanratkaisu konseptit
    9. Tiedostokahva
      objektit tiedostot
    10. Tiedostonimi
      merkkijonot tiedostot
    11. Tiedostopääte
      tiedostot
    12. Tietorakenne
      sanakirjat konseptit listat
    13. Tila
      konseptit
    14. Toistorakenne
      ohjausrakenteet
    15. Tosi
      arvot
    16. True
      Tosi
    17. Totuusarvo
      ohjausrakenteet
    18. Traceback
      ongelmanratkaisu
    19. Tulostaminen
      merkkijonot konseptit
    20. Tynkäfunktio
      ongelmanratkaisu funktiot
    21. Tyylisääntö
    22. Tyyppi
      arvot konseptit
    23. Tyyppimuunnos
      arvot funktiot tyypit
  20. V
    1. Vakio
      muuttujat arvot
    2. Valinnainen argumentti
      arvot funktiot parametrit
    3. Vertailuarvo
    4. Vertailuoperaattori
      ohjausrakenteet operaattorit
    5. Viittaaminen
      muuttujat arvot objektit
    6. Virheviesti
      ongelmanratkaisu
  21. W
    1. while
      toistorakenteet
    2. with
      tiedostot
Ratkaistu: / tehtävää

2. Materiaali: Muodonmuutoksia ja risteyksiä

Ennemerkit

Tässä materiaalissa teemme Pythonista laskimen sijaan kirjoituskoneen. Opimme myös edistyneempää kommunikaatiota ohjelman ja käyttäjän välillä. Koodi alkaa reagoida siihen, mitä ihminen tekee. Koneiden vallankumous on siis aivan nurkan takana.
Materiaalin aiheena on jälleen keskeisiä ohjelmointikäsitteitä: ehtolauseet ja merkkijonot, joista jälkimmäiseen tehtiin pintaraapaisu jo aiemmin. Kirjoitusteemaan sopivasti myös sanakirjat mahtuvat mukaan. Periaatteessa näillä työkaluilla voi jo tehdä jotain niinkin eeppistä kuin tekstiseikkailupelin. Me emme kuitenkaan tee tekstiseikkailua, koska sellaisen toteuttamisessa tekstin kirjoittaminen on merkittävästi suuremmassa roolissa kuin koodin. Sen sijaan mietimme läpi yleisiä ongelmaskenaarioita, joita ohjelmoinnissa tulee vastaan.
Useissa todellisissa ohjelmissa on yleensä jonkinlainen valikko, josta käyttäjä valitsee, mitä ohjelmalta haluaa. Tyypillinen ohjelma ei tee vain yhtä asiaa, vaan yleensä joukon toisiinsa liittyviä asioita. Esimerkiksi kuvankäsittelyohjelmista löytyy jos jonkinlaisia työkaluja, joita yhdistää se, että niillä kaikilla luodaan tai muokataan digitaalisia kuvia. Tämän materiaalin teemana ovatkin juuri valinnat. Toisaalta kysymys on siitä, miten käyttäjälle viestitään olemassaolevista valinnoista; toisaalta taas siitä, miten käyttäjältä voidaan selvittää, mitä tämä haluaa tehdä. Valintoihin liittyy tietenkin myös kolmas keskeinen kysymys: miten tietokone saadaan ohjeistettua tekemään valittu asia.

Yritys ja erehdys

Oppimistavoitteet: Tässä osiossa käydään läpi poikkeustilanteiden käsittelyä koodissa. Opimme uuden koodirakenteen, joka on tarkoitettu juuri tätä varten.
Viime materiaalissa uhkasimme, että päästämme jatkossa vähemmän täydelliset kädelliset sörkkimään niitä kauniisti toimivia ohjelmia, joita nähtiin materiaalin esimerkeissä sekä viikon harjoitustehtävissä. Mitä siis itse asiassa tapahtuu, jos koodissa yritetään muuttaa numeroksi jotain sellasta, joka ei näytä numerolta? Tämä voi tapahtua jo pelkästään siksi, että vitsaileva käyttäjä syöttää numerokenttään aasin. Asiaa on mukavampi käsitellä ykkösharjoitusten palloesimerkin kautta, joten palataan sen maailmaan. Koodi löytyy alta.
pallo.py
Anna pallon ympärysmitta: 23 cm
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
/joku/kansio/pallo.py in <module>()
     16     return ala, tilavuus
     17
---> 18 mitattu_piiri = float(input("Anna pallon ympärysmitta: "))
     19 laskettu_ala, laskettu_tilavuus = laske_pallon_ominaisuudet(mitattu_piiri)
     20 print("Tilavuus:", round(laskettu_tilavuus, 4))

ValueError: could not convert string to float: '23 cm'
Nyt syötetty
merkkijono
ei näytä numerolta, koska siihen on laitettu yksikkö perään. Tästä kertoo
virheviestin
viimeinen rivi. Koska virheviestit ovat tavallisille kuolevaisille pelottavuudeltaan Necronomiconin luokkaa, käyttäjien mielenterveyttä pitää suojella kiltimmän näköisillä huomautuksilla. Tähän suurten muinaisten vastaiseen toimintaan perehdytään seuraavaksi.

Poikkeusten filosofia

Koska emme voi tietää, mitä kaikkea käyttäjät keksivät ohjelmaan
syöttää
, täytyy ohjelmassa olla jokin tapa, jolla se käsittelee poikkeustilanteet. Tätä varten on Pythonissa, ja yleisesti ohjelmointikielissä, oma rakenteensa; puhutaan virheiden tai
poikkeusten
(engl. exception) käsittelystä.
Kuvalähteet 1
Perusajatuksena on, että koodia yritetään suorittaa normaalisti, mutta tiedostetaan samalla, että tiettyjä poikkeustilanteita voi syntyä, ja varaudutaan niihin. Tässäkin asiassa on kaksi puolta: ensiksi pitää selvittää, mitkä ovat mahdolliset poikkeustilanteet, ja toiseksi pitää tietää, miten niitä kohdattaessa toimitaan.
Kun kokeilimme syöttää kirjaimia ohjelmaan, saimme vastaukseksi tylyn ValueError-
viestin
. Ei ole olemassa mitään maagista keinoa, jolla selvittää kaikki poikkeustilanteet. Täytyy vain itse kokeilla kaikkea, mitä mieleen juolahtaa, ja toivoa, ettei käyttäjillä ole laajempi mielikuvitus.
Yleensä uusia ongelmatilanteita putkahtelee esiin ohjelman kehityksen edetessä, joten aiemmin kirjoitettua koodia saattaa joutua korjailemaan. Ei ole mitenkään harvinaista, että uusia virheitä löytyy senkin jälkeen, kun ohjelma on valmis. Esimerkiksi bugisia pelejä julkaistaan vähän väliä, koska laadunvalvonnassa ei ole aika ja mielikuvitus riittänyt yhtä pitkälle kuin tuhansien tai miljoonien pelaajien joukolla.
Pallokoodimme kohdalla meillä on kuitenkin jo tieto siitä, että yksi selkeä ongelmatilanne on se, kun käyttäjä
syöttää
jotain muuta kuin puhtaan numeron. Voimme siis tutustua
poikkeusten
käsittelyyn tämän esimerkkitapauksen kautta. Yksi hyvä tapa toimia tämän poikkeuksen kohdalla on ottaa
virheviestin
antama informaatio, ja tulostaa se käyttäjälle ystävällisemmässä muodossa. Ohjelma voi siis vaikka sanoa: "Syötteessä tulee olla pelkästään numeroarvo." Tämän jälkeen ohjelma voidaan lopettaa, minkä jälkeen käyttäjä pystyy suorittamaan sen uudelleen ohjeesta viisastuneesta. Eihän tämä ihanteellinen menettely ole, mutta parempaan kykenemme vasta myöhemmin.

Poikkeusten rakenne

Pallokoodin kyselyrivillä voi siis syntyä
poikkeus
, jos käyttäjä syöttää jotain muuta kuin float-
funktiolle
kelpaavan
merkkijonon
. Tästä pitäisi huomauttaa ystävällisesti käyttäjälle tulostamalla: "Syötteessä tulee olla pelkästään numeroarvo." Toteutus kiteytyy kolmeen
avainsanaan
, jotka muodostavat uudenlaisen rakenteen: try, except ja else. Näistä kolmesta viimeinen ei ole pakollinen osa rakennetta mutta usein tarpeellinen. Tämän rakenteen avulla määritetään:
  1. miltä riveiltä poikkeusta odotetaan (try)
  2. mitä poikkeuksia otetaan huomioon, ja mitä niiden ilmetessä tehdään (except)
  3. mitä tehdään, jos suoritus onnistuu ongelmitta (else)
Meidän tapauksessamme vastaukset näihin kysymyksiin olisivat:
  1. input-rivi
  2. ValueError, kerrotaan käyttäjälle syötteen olleen vääränlainen
  3. suoritetaan ohjelma normaalisti loppuun
Nämä kolme sijoitetaan rakenteeseen sisennettynä:
try:
    mitattu_piiri = float(input("Anna pallon ympärysmitta: "))
except ValueError:
    print("Syötteessä tulee olla pelkästään numeroarvo.")
else:
    laskettu_ala, laskettu_tilavuus = laske_pallon_ominaisuudet(mitattu_piiri)
    print("Tilavuus:", round(laskettu_tilavuus, 4))
    print("Pinta-ala:", round(laskettu_ala, 4))
Ensimmäisenä rakenteessa on siis try-
haara
, joka aloitetaan
avainsanalla
try sekä kaksoispisteellä. Varsinainen tarkistettava koodirivi, josta tiedämme
poikkeustilanteen
syntyvän, on try-haaran sisällä
sisennettynä
omaan
lohkoonsa
.
Seuraavana on except-haara. Tavallisesti exceptille tulee määritellä, minkä nimisen poikkeuksen se käsittelee. Tämä määritetään kirjoittamalla poikkeuksen nimi exceptin jälkeen, ennen rivin päättävää kaksoispistettä. Sisennettynä except-haaran sisällä on toimenpide, joka suoritetaan poikkeuksen tapahtuessa, eli tässä tapauksessa vikailmoituksen tulostaminen käyttäjälle.
Viimeisenä on else-
haara
, jonka sisälle on
sisennetty
loput ohjelman suorituksesta. Tässä lohkossa oleva koodi suoritetaan vain ja ainoastaan, jos try-haaran sisälle sijoitettu koodi suorittuu ongelmitta. Alla olevissa kahdessa animaatiossa on esitetty miten ohjelman suoritus etenee try-except-else-rakenteessa ja sen jälkeen.
Kuvalähteet 2
Kaikkia
poikkeuksia
ei ole tavallisesti tarkoitettu kiinniotettavaksi. Yleisesti ottaen oman koodin huonoudesta johtuvia ongelmia ei tulisi paikkailla virheenkäsittelyllä vaan paremmalla suunnittelulla. Poikkeuksia, jotka yleensä kertovat tämäntyyppisistä virheistä, ovat mm. TypeError ja NameError. NameError kohdattiin ohimennen ensimmäisessä materiaalissa kun yritettiin lukea
funktion
paikallisia
nimiä
pääohjelmassa
. Se kertoo, että yritetään käyttää
muuttujaa
, jota ei ole määritelty. Tämä voi johtua kirjoitusvirheestä, mutta myös siitä, että muuttuja määritetään sellaisessa
haarassa
, johon ei kaikissa tilanteissa mennä. Esimerkiksi jos tuosta yllä olevasta try-rakenteesta jätettäisiin else-osa pois:
try:
    mitattu_piiri = float(input("Anna pallon ympärysmitta: "))
except ValueError:
    print("Syötteessä tulee olla pelkästään numeroarvo.")
laskettu_ala, laskettu_tilavuus = laske_pallon_ominaisuudet(mitattu_piiri)
print("Tilavuus:", round(laskettu_tilavuus, 4))
print("Pinta-ala:", round(laskettu_ala, 4))
Kun käyttäjä
syöttää
kyselyyn virheellisen vastauksen, tulostetaan edelleen virheilmoitus. Tulostamisen jälkeen jatketaan koodiin, joka sijaitsee nyt else-
lohkon
sijaan try-rakenteen ulkopuolella. Ensimmäisellä try-rakenteen jälkeisellä rivillä yritetään lukea piiri-
muuttujan
arvoa
. Sen määrittely jäi kuitenkin tekemättä, joten syntyy NameError. Määrittelyä ei ennätetä tehdä try-rakenteessa, koska try-lohkon sisällä olevan koodin suoritus katkeaa float-
funktiokutsun
synnyttämään
poikkeukseen
. Virhe tässä tapauksessa on tietenkin se, että kaikkien kolmen viimeisen rivin tulisi olla else-haarassa, koska ne voidaan suorittaa ainoastaan, jos piiri-muuttuja saadaan luotua ohjelman suorituksen alussa.

Ohjelmistoväkivaltaa

Koska ohjelmat saattavat joskus jäädä jumiin, käyttäjille on hyvä olla olemassa keino, jolla ne saadaan keskeytettyä väkipakolla muutenkin kuin koneen virtanapista. Terminaaliohjelmille tämä keino on näppäimistökeskeytyksen aiheuttaminen. (Viimeistään) tässä tehtävässä opimme miten tuo keskeytys aiheutetaan ja mitä se saa aikaan ohjelman näkökulmasta.
Opittavat asiat: Miten keskeyttää ajossa oleva ohjelma, ja minkä niminen
poikkeus
siitä syntyy ohjelman sisällä.

Alustus:
Kirjoita
tulkkiin
seuraavat kaksi riviä ja vasaroi enteriä kunnes ...:-merkkejä ei enää näy:
In [1]: while True:
   ...:     pass
   ...:  
   ...: 
Nämä kaksi riviä aiheuttavat
ikuisen silmukan
, jossa tietokone on jumissa tekemässä ei-mitään. Keskeytä silmukka painamalla Ctrl + C.

Haettu vastaus:
Vastaukseen haetaan näppäinyhdistelmän aiheuttaman poikkeuksen nimiä. Kopioi se siis virheviestistä vastauslaatikkoon.
Poikkeuksen nimi tähän laatikkoon.
Varoitus: Et ole kirjautunut sisään. Et voi vastata.

Aasilla ratsastava ohjelma

Tässä tehtävässä toteutamme äärimmäisen yleisen ohjelmarakenteen, jossa äsken käsitelty näppäimistökeskeytys käsitellään ilman kohtausta. Toisin sanoen teemme ohjelman, joka sanoo kiltisti "terve ja kiitos kaloista" sen sijaan, että räjäyttäisi virhelitaniat ruudulle väkivaltaa kohdatessaan.
Opittavat asiat:
Poikkeuksen
käsittelyn perusrakenteen toteuttaminen sekä hyvin yleiseen
pääohjelma
rakenteeseen tutustuminen.

Alustus:
Jotta pääohjelman koodia voidaan mielekkäästi testata, kooditiedostossa tulisi olla jokin
funktio
, jonka toiminnan voi keskeyttää Ctrl + C -näppäinyhdistelmällä. Alla on esitetty hyvin yksinkertainen ohjelma, josta puuttuu poikkeusten käsittely.
def valikko():
    print(input(": "))

valikko()
Jos olisimme tekemässä ihan oikeaa ohjelmaa, tuonne valikko-funktion sisälle tulisi ohjelman päävalikon toimintalogiikka. Nyt siellä on pelkkä syötteen kysely, jotta ohjelman keskeyttämistä voi testata.

Toteutettava pääohjelma:
Tavoitteena on lisätä ylläolevaan koodiin yksinkertainen try-except-rakenne, jossa valikko-funktiota
kutsutaan
try:n sisällä. Rakenteen except-
osalla
puolestaan otetaan kiinni edellisessä tehtävässä nimen saanut poikkeus. Mikäli tämä poikkeus kohdataan, ohjelma tulostaa "Terve ja kiitos kaloista". Tällä kertaa rakenteen else-osaa ei ole tarpeen määritellä.
Varoitus: Et ole kirjautunut sisään. Et voi vastata.

Kohtalokkaat valinnat

Kuten alussa oli puhetta, on yleistä, että missään vähänkään laajemmassa ohjelmassa on jonkinlainen valikko. Meidän pitäisi siis varmaankin tehdä sellainen. Maailman suurimpiin mysteereihin lukeutuvat imperiaaliset yksiköt, joissa ei ole niin minkäänlaista järjellistä logiikkaa. Otamme tavoitteeksi tehdä ohjelman, joka muuntaa joitain yleisimpiä hyväntuulisen-aasin-korvien-väli-keskikesällä-tyyppisiä yksiköitä SI-järjestelmään.
Valitaan siis tehtäväksi ohjelma, joka osaa muuntaa seuraavat yksiköt:
Kyseessä on ensimmäinen hieman monimutkaisempi ohjelma tällä kurssilla. Tämä tarkoittaa, että pääsemme ensimmäistä kertaa oikeasti miettimään, millä tavalla ohjelma kannattaa toteuttaa. Tarvittavat laskutoimitukset ovat tiedossa, mutta lisäksi pitäisi päättää, millä tavalla ohjelmaa käytetään.
Osaamistavoitteet: Tässä osiossa opimme, miten ohjelman suoritusta voidaan ohjata riippuen käyttäjän syötteistä. Oleellisena osana tätä on ehtorakenteiden hallinta, ja tämän osion jälkeen tiedätkin, mitä ne ovat ja miten niitä käytetään valikkorakenteiden toteuttamiseen. Opit myös käskyn, joka ei tee mitään, ja miten sitä voi käyttää apuna kun hahmotellaan ohjelman rakennetta funktioilla.
Kuvalähteet 3

Valintojen maailma

Ohjelmamme toteutustavan kannalta oleellinen kysymys on, miten se selvittää käyttäjän aikeet. Tässä tapauksessa siis pitäisi tavalla tai toisella selvittää, mitä yksikköä ollaan muuntamassa. Koska pystymme ainoastaan kyselemään tekstimuotoisia
syötteitä
, pääasiallisia vaihtoehtoja on kaksi: kysytään käyttäjältä, mitä yksikköä hän haluaa muuntaa, tai pyydetään sisällyttämään yksikön lyhenne syötteeseen ja tulkitaan siitä (esim. "5 oz"). Jälkimmäinen on mahdollisesti miellyttävämpi käyttää. Sen toteuttaminen vaatii kuitenkin enemmän uusia käsitteitä kuin olemme valmiit tämän materiaalin puitteissa käsittelemään. Tyydytään siis ratkaisuun, jossa käyttäjä valitsee ensin yksikön ja vasta sitten syöttää muunnettavan lukuarvon.
Lisäksi, koska vaihtoehtoja on useita, jaetaan ne kategorioihin pituus, massa, tilavuus ja lämpötila. Tällöin saadaan kaksitasoinen valikko, jossa ensin valitaan kategoria ja sen jälkeen yksikkö. Lopulta käyttäjä syöttää numeroarvon ja ohjelma laskee vastaavan arvon SI-yksiköissä. Ohjelma
haarautuu
siis kuvan esittämän kaavion mukaisesti.
Kaavio ohjelman haarautumisesta. Pääohjelmasta lähtee neljä haaraa: pituus, paino, tilavuus ja lämpötila. Pituus-haara jakaantuu vielä haaroihin: tuuma, jalka, jaardi ja maili. Paino jakautuu haaroihin: unssi ja pauna. Tilavuus jakautuu haaroihin: kupillinen, pintti, varttigallona, gallona. Lämpötila ei jakaudu uusiin haaroihin.
Ohjelman suunniteltu haarautuminen eri toimintoihin.
Koska meillä on vain yksi (mutta sitäkin hämmentävämpi) lämpötilayksikkö, sen kohdalla valikko on yksitasoinen. Suunnittelun jälkeen pitäisi viimein opetella, miten ohjelman suorittamista voi tällä tapaa ohjata eri haaroihin. Tähän tarkoitukseen ohjelmointikielissä on ehtorakenteet.

Ehdottomasti ehkä

Ehtorakenne
on nimensä mukaisesti ehdoista koostuva rakenne, jossa kunkin ehdon toteutumisesta seuraa jotain.
Ehtolauseet
muotoutuvat hyvin pitkälti luonnollisen kielen mukaisesti: "jos keskiyöllä on deadline, nyt koodataan". Aikaisemmin käsittelimme try-rakennetta, joka on tietyllä tapaa ehtorakenteen erikoistapaus: "jos kahvin keitto ei onnistu, keitä teetä; jos onnistuu, juo kahvia", jossa siis "kahvin keitto" olisi try-
haara
, "keitä teetä" except-haara ja "juo kahvia" else-haara. Tässä rakenteessa kuitenkin ns. ehtoina toimivat Pythonin heittämät
poikkeukset
siinä, missä ehtorakenteissa keskitytään tarkastelemaan (yleensä)
muuttujien
tai
lausekkeiden
arvoja
tavalla tai toisella. Voidaan esimerkiksi tarkastella, onko käyttäjän antama
syöte
identtinen "pituus"-
merkkijonon
kanssa. Koodissa tämä tapahtuisi if-lauseella:
if valinta == "pituus":
Tässä valinta on siis muuttuja, johon käyttäjän syöte on tallennettu (ts. muuttuja, joka viittaa käyttäjän antamaan syötteeseen). Tällä rivillä varsinainen tarkasteltava
ehto
on puolestaan valinta == "pituus", joka on arvioitava lauseke. == on yhtäsuuruutta vertaileva
operaattori
. Se palauttaa boolean-tyyppisen
totuusarvon
, jolla on olemassa vain kaksi
literaaliarvoa
: True ja False.
Ohjelmoinnissa if-lause on
avainsana
, joka tarkastelee sille annetun ehdon totuusarvoa: jos ehdon totuusarvo on True tai sitä vastaava arvo, suoritetaan if-lauseen alla määritelty toiminnallisuus; jos totuusarvo on False tai vastaava arvo, jätetään if-lauseen alla määritelty toiminallisuus suorittamatta. Tällöin jatketaan seuraavalta sellaiselta koodiriviltä, joka on samassa tasossa if-lauseen kanssa eli ehtorakenteen ulkopuolella.
Havainnollistetaan asiaa animaatioilla. Esimerkkikoodissa negatiiviset kappalemäärät sekä nolla muutetaan ykköseksi ennen kuvitteelliseen katalogiin lisäämistä.
Näissä animaatioissa oli siis kaksi tärkeää asiaa: se, miten
if-rivillä
oleva lauseke palautuu aina lopulta yksittäiseksi
arvoksi
, jonka totuudellisuutta arvioidaan; ja se, miten ohjelman suoritus etenee riippuen siitä, onko
ehto
tosi vai epätosi. Animaatioissa esiintyy myös uudenlainen käyttötapa print-
funktiolle
, johon palataan myöhemmin. Lisäksi havaitaan, että itse if-lauseessa on käytetty uutta
operaattoria
: <. Matematiikasta tuttuun tapaan kyseessä on pienempi kuin -operaattori. Alla olevaan tauluun on koottu kaikki vastaavat operaattorit, joilla vertaillaan arvoja keskenään.
== a == b a on yhtä suuri kuin b
!= a != b eri suuri kuin
< a < b a on pienempi kuin b
<= a <= b a on pienempi tai yhtä suuri kuin b
> a > b a on suurempi kuin b
>= a >= b a on suurempi tai yhtä suuri kuin b

Tosi kyseessä

Opittavat asiat:
Vertailuoperaattorien
merkitys.

Alustus:
Olkoon meillä seuraavanlainen lauseke, josta operaattori on korvattu X:llä:
5 X 4

Haettu vastaus:
On olemassa kolme vertailuoperaattoria, joita käyttämällä em. rivi palauttaa True. Kirjoita nämä kolme operaattoria vastauslaatikkoon alekkain.
Kirjoita pelkät operaattorit laatikkoon, kukin omalle rivilleen.
Varoitus: Et ole kirjautunut sisään. Et voi vastata.
Huomioitavia asioita:
In [1]: 1 == 1.0
Out[1]: True
In [2]: 1 == "1"
Out[2]: False
Siinä missä kokonaisluvut ja
liukuluvut
voivat olla keskenään yhtä suuria,
merkkijono
ei voi koskaan olla yhtä suuri kuin kokonaisluku tai liukuluku. Tämän muistaminen on tärkeää, koska käyttäjän
syötteiden
vertailu
if-lauseissa
voi tuottaa hämmentäviä Falseja, mikäli on unohtanut muuttaa ne luvuiksi. Tietenkin erisuuruutta vertailtaessa asetelma kääntyy toisin päin:
In [1]: 1 != 1.0
Out[1]: False
In [2]: 1 != "1"
Out[2]: True
Eri tietotyyppien suuruutta ei voi vertailla muilla
operaattoreilla
:
In [1]: "koirat" < 3
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-5-7396907f20c6> in <module>()
----> 1 "koirat" < 3

TypeError: unorderable types: str() < int()
Hämmentävää sen sijaan voi olla se, että merkkijonoja voidaan vertailla keskenään:
In [1]: "aasi" > "mursu"
Out[1]: False
In [2]: "apina" > "aasi"
Out[2]: True
Merkkijonojen "suuruus" tässä tapauksessa perustuu niiden vertailuun aakkosjärjestyksen avulla. Yleinen skenaario tässäkin on se, että kysyt käyttäjältä kaksi lukua ja vertailet niitä keskenään, mutta unohdat
muuttaa
ne luvuiksi jolloin:
In [1]: "31" > "4"
Out[1]: False
╯°□°)╯︵ ┻━┻

Tarkistussumma

Blackjackissa yksi oleellinen tarkistus on se, meneekö korttien summa yli 21:n. Jos siis olisimme tekemässä Blackjack-ohjelmaa, siellä todennäköisesti tarvittaisiin tätä tarkistusta varten ehtolause jossain vaiheessa. Tehtäväsi on selvittää miten tämä ehtolause kirjoitettaisiin.
Opittavat asiat: Numeroita vertailevan
ehtolauseen
kirjoittaminen.

Alustus:
Haemme ehtolausetta, joka sopii tähän funktioon:
def ylitys(summa):
    # ehtolause tähän
        return True
    else:
        return False
Ehtolauseen tulee siis toteutua jos summa-muuttujan arvo on suurempi kuin 21.

Haettu vastaus:
Kun olet keksinyt mielestäsi toimivan ehtolauseen, kopioi se ilman sisennystä vastauslaatikkoon. Älä käytä vastauksessasi turhia sulkuja.

Resurssit:
Jos haluat, voit myös testata ehtolauseen toimivuutta tämän ohjelmapätkän kanssa:
blackjack_the_very_beginning.py
def ylitys(summa):
    # ehtolause tähän
        return True
    else:
        return False

summa = int(input("Anna käsikorttien summa: "))
if ylitys(summa):
    print("Hävisit")
else:
    print("Et hävinnyt, ainakaan vielä...")

Vastaukseen siis pelkkä if-rivi.
Varoitus: Et ole kirjautunut sisään. Et voi vastata.

Kolmikärki

Yksittäinen
ehtolause
ei ole vielä kovin tehokas rakenne. Onneksi
ehtorakenteisiin
saa useita
haaroja
. Kun katsotaan uudestaan aiemmin esitettyä kaaviota yksikkömuunnosohjelmamme toimintojen haarautumisesta, huomataan, että ylimmällä tasolla tarvittaisiin ehtorakenne, jossa on neljä eri vaihtoehtoa. Näitä vaihtoehtoja varten täytyy myös päättää, millä tavalla käyttäjä valintansa tekee. Yksinkertaisimmillaan käyttäjää voidaan pyytää kirjoittamaan "pituus", "tilavuus", "massa" tai "lämpötila", mikä on ainakin hyvin yksiselitteistä. Aloitetaan tällä ratkaisulla.
Hyvä tapa luoda valikkorakenne on käyttää
funktioita
apuna. Jokainen ohjelman osa saa oman funktionsa, ja päävalikossa ainoastaan
kutsutaan
näitä funktioita. Tällä tavalla itse valikkokoodi jää hyvin selkeäksi. Vastaavasti kaikki ohjelman osatkin löytyvät omista funktioistaan, missä niitä on helpompi muokata kuin osana
pääohjelmaa
, johon sijoitamme ehtorakenteemme. Aivan erityisesti ehtorakenne on helpompi hahmottaa, jos yhden ehdon sisällä ei ole kovin montaa riviä koodia. Aloitetaan siis ohjelmamme suunnittelu siten, että luodaan jokaiselle osaohjelmalla oma funktionsa, noudattaen alkuperäistä haarautumiskaaviota:
def pituus():

def massa():

def tilavuus():

def lampotila():
Tällä kertaa
funktioilla
ei ole lainkaan
parametrejä
. Tätä tapahtuu erityisesti juuri valikkomaisissa ratkaisuissa, koska mitään eteenpäin siirrettävää tietoa ei vielä ole. Virheetön tämä ratkaisu tosin ei ole, sillä yllä olevan koodin suorittamisesta syntyy IndentationError. Kaksoispistettä onkin aina seurattava vähintään yksi
sisennetty
rivi. Välillä on silti mukava pystyä koodin jäsentämiseksi kirjoittamaan funktiomäärittelyt valmiiksi ennen kuin miettii yhtään niiden sisällöstä. Tätä varten löytyy onneksi Pythonista hyödyllinen ominaisuus: käsky, joka ei tee yhtään mitään, eli pass. Käytämme sitä väliaikaissisältönä funktioissa, jotta koodi voidaan suorittaa, vaikka funktiot eivät ole vielä valmiita.
def pituus():
    pass

def massa:()
    pass

def tilavuus():
    pass

def lampotila():
    pass
Ehtorakenteisiin
voidaan lisätä uusia
haaroja
käyttämällä elif-lausetta (sanoista else if). Nämä lauseet tulevat samalle
sisennystasolle
kuin rakenteen aloittanut
if-lause
ja toimivat täsmälleen samalla tavalla. Ehtorakenteessa elif-lauseen
koodilohko
voidaan kuitenkin suorittaa vain, jos mikään sitä edeltänyt (eli yläpuolella oleva) osa ehtorakenteesta ei ole toteutunut. Toisin sanoen tämä tarkoittaa, että heti, kun ehtorakenteen yhdenkin haaran
ehto
toteutuu, loppuja ehtoja ei edes tarkastella.
luku = int(input("Anna kokonaisluku: "))
if luku < 10:
    print("Luku on pienempi kuin 10")
elif 10 <= luku < 100:
    print("Luku on pienempi kuin 100")
elif 100 <= luku < 1000:
    print("Luku on pienempi kuin 1000")
Pintapuolisesti tämä näyttää täysin järkevältä. Kuitenkin kun muistetaan, että elif sisältää oletuksen, että sitä edeltävät ehdot eivät ole toteutuneet, havaitaan, että kummallakin esimerkin elif-rivillä ensimmäinen vertailu on turha.
Tämä perustuu siihen, että kummassakaan elif-lauseessa ei oltaisi, jos luku olisi ollut pienempi kuin 10, eikä viimeisessä oltaisi, jos luku olisi ollut pienempi kuin 100. Esimerkki voidaankin siis kirjoittaa tämän tiedon valossa seuraavasti:
luku = int(input("Anna kokonaisluku: "))
if luku < 10:
    print("Luku on pienempi kuin 10")
elif luku < 100:
    print("Luku on pienempi kuin 100")
elif luku < 1000:
    print("Luku on pienempi kuin 1000")

Todistustaakka

Opittavat asiat: Näemme, että
ehtorakenteen
haaroista
valitaan todellakin vain yksi.

Alustus:
Lataa esimerkin koodi resurssilinkistä ja suorita se kaksi kertaa. Syötä ohjelmaan ensimmäisellä kerralla 8 ja toisella 108.

Haettu vastaus:
Kopioi vastauslaatikkoon kummankin suorituskerran tulostetut rivit (huom. älä kopioi mukaan inputin kysymystä äläkä sen perään kirjoitettua
syötettä
).

Resurssit:
if_esim.py
luku = int(input("Anna kokonaisluku: "))
if luku < 10:
    print("Luku on pienempi kuin 10")
elif luku < 100:
    print("Luku on pienempi kuin 100")
elif luku < 1000:
    print("Luku on pienempi kuin 1000")

Kopioi tulosteet vastaukseen.
Varoitus: Et ole kirjautunut sisään. Et voi vastata.
Nyt kun tiedämme, miltä useita
haaroja
sisältävä
ehtorakenne
näyttää
elif-lauseita
käyttäen, voimme hahmotella päävalikkokoodin:
valinta = input("Tee valintasi: ")
if valinta == "pituus":
    pituus()
elif valinta == "massa":
    massa()
elif valinta == "tilavuus":
    tilavuus()
elif valinta == "lämpötila":
    lampotila()
Valikon rakenne on hyvin selkeä. Ehtorakenne ohjaa ohjelman suorituksen eri toiminnoista vastaaviin
funktioihin
sen perusteella, mitä käyttäjä kirjoittaa
syötteeksi
. Koska funktiot on määritelty (vaikka ne eivät mitään teekään), tämä ohjelma voidaan suorittaa. Tällä tavalla ohjelman koodaamisen voi osittaa hyvin pieniin palasiin ja joka käänteessä pystyy aina tarkistamaan, onko koodiin jäänyt virheitä. Funktioihin voisi jopa kirjoittaa jotain tämän tapaista:
def pituus():
    print("Valittiin pituus")
Ohjelmaa ajamalla voidaan nyt tulosteesta nähdä, että valitsemalla "pituus" ohjelma todellakin etenee pituus-funktioon. Tämä on erittäin hyvä tapa etsiä virheitä
ehtorakenteista
, joissa on monimutkaisia ja virheherkkiä
ehtoja
.
Mihin sitten tarvitaan juuri
elif-lausetta
? Eikö riittäisi, että käyttää montaa if-lausetta? Ero on siinä, että if aloittaa uuden ehtorakenteen siinä, missä elif jatkaa ehtorakennetta. Jokaisesta ehtorakenteesta suoritetaan korkeintaan yksi
haara
, ja ehtoja tarkistetaan vain siihen haaraan asti, joka suoritetaan. Jos siis valikkoesimerkissämme käyttäjä valitsee "pituus", muita ehtoja ei tarvitse tarkistaa. Jos sen sijaan olisimme tehneet valikon pelkillä if-lauseilla, kaikki ehdot tarkistettaisiin aina, koska siinä olisi yhden neljähaaraisen ehtorakenteen sijaan neljä yksihaaraista. Tässä kyseisessä tapauksessa toiminnassa ei tapahdu eroa, koska ehdoissa ei ole päällekkäisyyttä (merkkijono ei voi olla samaan aikaan yhtäsuuri kuin "pituus" ja "massa"). Päällekkäisyyttä sisältävät ehdot demonstroivat eron paremmin:

Ehtoarkkitehti

Ehtorakenteissa haarojen järjestyksellä on merkitystä. Jokainen toteutumaton ehto luo myös tietoa, jota voidaan käyttää hyödyksi myöhemmissä haaroissa. Jos vaikka ensimmäisessä haarassa testataan onko eläin aasi, kaikissa myöhemmissä haaroissa voidaan olla varmoja siitä että eläin ei ollut aasi - jos nimittäin se olisi ollut aasi, ensimmäinen ehto olisi toteutunut ja muita haaroja ei edes kokeilla! Tässä tehtävässä pääset itse tutkimaan ehtojen järjestyksen merkitystä ja hyödyntämään tätä informaation kertymistä.
Opittavat asiat: Moni
haaraisen
ehtorakenteen
kirjoittaminen.
Ehtolauseiden
järjestäminen rakenteen sisällä
.
Tavoite: Ohjelma, joka kertoo toteamuksen annetun luvun suuruudesta.

Alustus:
Koska ehtorakenteen kirjoittaminen on mielekkäämpää, jos on jotain tarkisteltavaa, aloita kooditiedostosi tällä rivillä:
luku = int(input("Anna kokonaisluku: "))

Toteutettava pääohjelma:
Tehtävänäsi on nyt, käyttäen ehtorakenteista oppimiasi asioita, kirjoittaa
pääohjelmaan
ehtorakenne, joka arvioi luvun suuruutta seuraavin toteamuksin:
  • Luku on suurempi kuin 10
  • Luku on suurempi kuin 100
  • Luku on suurempi kuin 1000
Koodisi saa tulostaa näistä kerrallaan vain yhden, ja sen tulee olla aina suurin mahdollinen (eli luvulle 256 pitäisi tulostaa keskimmäinen). Koodissasi ei myöskään saa olla turhia tarkistuksia - vain yksi vertailu on sallittu ehtolausetta kohti. Kiinnitä siis huomiota
ehtojen
järjestykseen koodissa ja hyödynnä sitä!
Varoitus: Et ole kirjautunut sisään. Et voi vastata.

Käyttökokemus on ehdoton

Meillä on nyt siis koodi, joka määrittelee ohjelmamme aloitusvalikon
ehtorakenteen
avulla. Kovin käytettävä se ei ole. Käyttäjä ei mm. tiedä, mitä vaihtoehtoja ohjelmassa on. Tämä voidaan korjata kirjoittamalla hieman ohjeita ennen
syötteen
kysymistä. Lisätään siis tulostusrivejä
pääohjelman
alkuun:
print("Tämä ohjelma muuntaa yhdysvaltalaisia yksiköitä SI-yksiköiksi")
print("Mahdolliset toiminnot:")
print("pituus")
print("massa")
print("tilavuus")
print("lämpötila")
print()
valinta = input("Tee valintasi: ")
if valinta == "pituus":
    pituus()
elif valinta == "massa":
    massa()
elif valinta == "tilavuus":
    tilavuus()
elif valinta == "lämpötila":
    lampotila()
Viimeinen print-
kutsu
, jolla ei ole
argumentteja
, tuottaa yhden tyhjän rivin. Tyhjät rivit palvelevat koodissa sen selkeyttämistä, ja sama pätee myös käyttäjälle näytettävään informaatioon.
Entä jos käyttäjä syöttää tahallisesti tai vahingossa jotain, mikä ei kuulu
ehtorakenteessa
määriteltyihin
vaihtoehtoihin
? Tällä hetkellä ohjelma vain palauttaa käyttäjän
komentoriville
sanomatta mitään. Olisi parempi, jos ohjelma osaisi todeta: "Valitsemaasi toimintoa ei ole olemassa". Tähän tarkoitukseen sopii ehtorakenteiden kolmas komponentti, eli else. Else kattaa kaikki tilanteet, joita sitä edeltäneet
haarat
eivät kata, ja sen tulee olla aina ehtorakenteessa viimeisenä. Koska koodimme toimi aiemmin ilman elseä, pitäisi olla selvää, että se ei ole pakollinen. Otetaan tämä työkalu käyttöön:
if valinta == "pituus":
    pituus()
elif valinta == "massa":
    massa()
elif valinta == "tilavuus":
    tilavuus()
elif valinta == "lämpötila":
    lampotila()
else:
    print("Valitsemaasi toimintoa ei ole olemassa")
Else on ehtorakenteen yksinkertaisin osa, koska sille ei ole mahdollista määritellä
ehtoa
. Kaikessa yksinkertaisuudessaan else kattaa "kaikki muut tapaukset".
Nyt ohjelma on varsin ystävällinen. Se antaa ohjeita ja osaa myös sanoa jotain, kun asiat menevät pieleen. Kokeillaan!
Kuvalähteet 4

Koolla on väliä

Opittavat asiat: Näemme, että kirjainten koolla on merkitystä.

Alustus:
Lataa esimerkin ohjelma resurssit-kohdasta koneellesi. Kokeile käynnistää se ja antaa syötteeksi "Pituus".

Haettu vastaus:
Kopioi vastauslaatikkoon ohjelman antama vastaus.

Resurssit:
muuntaja.py
def pituus():
    print("Valittiin pituus")
    
def paino():
    print("Valittiin paino")
    
def tilavuus():
    print("Valittiin tilavuus")

def lampotila():
    print("Valittiin lämpötila")

print("Tämä ohjelma muuntaa yhdysvaltalaisia yksiköitä SI-yksiköiksi")
print("Mahdolliset toiminnot:")
print("pituus")
print("paino")
print("tilavuus")
print("lämpötila")
print()
valinta = input("Tee valintasi: ")
if valinta == "pituus":
    pituus()
elif valinta == "paino":
    paino()
elif valinta == "tilavuus":
    tilavuus()
elif valinta == "lämpötila":
    lampotila()
else:
    print("Valitsemaasi toimintoa ei ole olemassa")

Tähän laatikkoon.
Varoitus: Et ole kirjautunut sisään. Et voi vastata.

Merkillistä metodologiaa

Törmäsimme ongelmaan: jos käyttäjä antaa periaatteessa oikean komennon, mutta aloittaa sen isolla alkukirjaimella, ohjelma ei tunnista komentoa oikein. Tässä voisi tietysti todeta: "Tyhmä käyttäjä, et vain osaa". Kuitenkin, jos mahdollista, kannattaa hyväksyä myös muunlaiset kirjoitusasut sallituille sanoille. Ongelman ydin on siinä, että isot ja pienet kirjaimet ovat eri symboleita keskenään:
In [1]: "A" == "a"
Out[1]: False
Yksi idea olisi lisätä ehto, joka tunnistaa
merkkijonon
"Pituus". Entä jos käyttäjällä on caps lock päällä, ja hän
syöttääkin
"PITUUS" tai "pITUUS"? Olisi kenties parempi, jos vertailusta saataisiin sellainen, että kirjainten koolla ei ole merkitystä. Tämän toteuttamiseksi voimme muuttaa käyttäjän syötteen kaikki kirjaimet pieniksi. Tähän operaatioon tarvitaan
metodia
.
Oppimistavoitteet: Tässä osiossa opitaan, mitä ovat metodit ja mitä niillä voi tehdä merkkijonojen tapauksessa.

Hei, olen metodi

Metodeja (eng. method) kutsutaan myös jäsenfunktioiksi. Metodit ovat nimittäin myös
funktioita
. Ne eroavat aiemmin käytetyistä funktioista siinä, että metodi on kiinnitetty
objektiin
ts. se on jonkin objektin jäsen eli jäsenfunktio. Objekti (eng. object) tarkoittaa Pythonin kontekstissa mitä tahansa
arvoa
. Käytämme kuitenkin sanaa objekti, koska on helpompaa mieltää, että jollakin objektilla on
ominaisuuksia
, kuin että arvolla on ominaisuuksia. Objektista käytetään myös "oikeampaa" suomennosta olio. Mitä tämä kaikki sitten käytännössä tarkoittaa? Otetaan esimerkki:
luku = round(luku)
Useimmille funktioille määritellään objekti, jota ne käsittelevät,
argumenttien
kautta. Tässä esimerkissä round-funktio käsittelee objektia, joka on luku-
muuttujassa
(joka on siis sama asia kuin luku-muuttujan arvo). Metodi eroaa tästä siten, että metodi käsittelee sitä objektia, jonka jäsen se on. Alla on käytetty malliksi
tulkissa
metodia strip. Esimerkkiä ei tarvitse vielä täysin ymmärtää, mutta voit jo kokeilla sitä itse!
In [1]: otus = "   aasi    "
In [2]: otus.strip()
Out[2]: 'aasi'
Objektin
metodit
riippuvat sen
tietotyypistä
. Siispä esim. kaikilla
merkkijonoilla
on samat metodit. Lista niistä löytyy Pythonin dokumentaatiosta. Vaihtoehtoisesti ne näkee myös tulkissa dir-
funktiolla
, joka listaa objektin kaikki
ominaisuudet
(attribute), joihin metodit kuuluvat. Sen voi tehdä esimerkiksi antamalla dir-funktiolle
argumentiksi
tyhjän merkkijonon:
In [1]: dir("")
Out[1]:
['__add__',
 '__class__',
 '__contains__',
 '__delattr__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__getitem__',
 '__getnewargs__',
 '__gt__',
 '__hash__',
 '__init__',
 '__iter__',
 '__le__',
 '__len__',
 '__lt__',
 '__mod__',
 '__mul__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__rmod__',
 '__rmul__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 'capitalize',
 'casefold',
 'center',
 'count',
 'encode',
 'endswith',
 'expandtabs',
 'find',
 'format',
 'format_map',
 'index',
 'isalnum',
 'isalpha',
 'isdecimal',
 'isdigit',
 'isidentifier',
 'islower',
 'isnumeric',
 'isprintable',
 'isspace',
 'istitle',
 'isupper',
 'join',
 'ljust',
 'lower',
 'lstrip',
 'maketrans',
 'partition',
 'replace',
 'rfind',
 'rindex',
 'rjust',
 'rpartition',
 'rsplit',
 'rstrip',
 'split',
 'splitlines',
 'startswith',
 'strip',
 'swapcase',
 'title',
 'translate',
 'upper',
 'zfill']
Funktio palauttaa listan ominaisuuksia. Alkupään ominaisuudet, joiden nimiä koristavat alaviivat, on tarkoitettu ainoastaan objektin sisäiseen käyttöön, ja niitä käytännössä käsittelee vain Python itse. Niistä ei siis tarvitse välittää. Loput ovat (tässä tapauksessa) metodeja, jotka ovat kiinnostuksemme kohteena. Kuten funktioilla, myös niillä on enemmän tai vähemmän kuvaavat nimet. Otetaan metodien käytöstä toinen yksinkertainen esimerkki, joka sattumoisin myös ratkaisee ongelmamme:
In [1]: sana = "AaSi"
In [2]: sana.lower()
Out[2]: 'aasi'
Esimerkin voisi myös lyhentää muotoon "AaSi".lower() -
Metodikutsun
voi siis liittää myös
literaaliarvon
perään. Piste erottaa
objektin
ja
metodin
toisistaan. Metodikutsun ensimmäinen osa siis kertoo, mistä objektista kutsuttava metodi löytyy, ja pisteen jälkeinen osa varsinaisen metodin nimen.
Huomattavaa on, että esimerkissä metodikutsun sulut ovat tyhjät. Tämä on seurausta juuri siitä, että metodi käsittelee objektia johon se kuuluu. Se informaatio, joka
funktiokutsussa
olisi sulkujen sisällä, on nyt pisteen vasemmalla puolella. Tämä ei tarkoita, etteikö olisi metodeja joille annetaan
argumentteja
– onhan meillä funktioitakin, joille kerrotaan enemmän kuin yksi käsiteltävä arvo.
Otetaan kolmantena esimerkkinä count-
metodi
, jolla voidaan laskea, montako kertaa lyhyempi
merkkijono
esiintyy toisen, pidemmän merkkijonon sisällä. Metodin toimintaa voi tarkastella Pythonin dokumentaatiosta tai sitten
tulkissa
:
In [1]: help("".count)
Help on built-in function count:

count(...) method of builtins.str instance
    S.count(sub[, start[, end]]) -> int

    Return the number of non-overlapping occurrences of substring sub in
    string S[start:end].  Optional arguments start and end are
    interpreted as in slice notation.
Rivillä, joka näyttää miten metodia käytetään, on ennen pistettä S. Tähän paikalle tulee siis aina pidempi merkkijono, josta toista lyhyempää merkkijonoa etsitään. Suluissa puolestaan on pakollinen
argumentti
sub sekä valinnaiset start ja end (hakasulut siis kertoivat valinnaisuudesta). Näistä sub on merkkijono, jota etsitään S-merkkijonon sisältä. Valinnaisilla argumenteilla voidaan rajata etsintä jollekin tietylle välille S-merkkijonossa.
Tällä metodilla voidaan mm. laskea montako
vertailuoperaattoria
kooditiedostosta
löytyy, tai löytyykö koodista oikea määrä elif-lauseita
ehtorakenteista
. Tätä ja muita koodin tarkastelua kutsutaan staattiseksi tarkistamiseksi, jossa koodia luetaan tekstinä suorittamisen sijaan. Kyseessä on tietenkin vain yksi käyttötarkoitus
merkkijonojen
tutkimiselle. Tekstin tulkitseminen tietokoneella on merkittävä osa-alue tietotekniikkaa, ja myös sen voi aloittaa näin yksinkertaisilla tempuilla:
teksti = input("Kirjoita tähän runo: ")
aasit = teksti.count("aasi")
print("Tekstistä löytyi", aasit, "aasi(a)")

Meta-if

Tässä tehtävässä pääset tutustumaan tarkistimien kirjoittajien arkeen. Pitäisi nimittäin selvittää onko opiskelijan kirjoittamassa ehtolauseessa kaksoispiste lopussa.
Opittavat asiat:
Metodien
käyttö
ehtolauseissa
. Metodien etsintää dokumentaatiosta harrastetaan jälleen, koska se on tärkeää.

Alustus:
Jotta tehtävän tekeminen olisi mielekkäämpää, on hyvä olla jälleen jotain tarkistettavaa. Aloitetaan siis
sijoittamalla
merkkijono
muuttujaan
jota voidaan tarkistella ehtolauseessa:
In [1]: rivi = "if elain == \"aasi\":"

Haettu vastaus:
Vastauksen tulee olla ehtolause, joka selvittää päättyykö rivi kaksoispisteeseen - tämä onnistuu sopivalla
merkkijonometodilla
(löydät linkin merkkijonometodien listaan kun viet kursorin termin päälle!). Toisin sanoen yllä annetun rivi-muuttujan pitäisi antaa ehtolauseesta True. Käytä tätä muuttujaa vastauksessasi, ja muista lopettaa oma ehtolauseesi kaksoispisteeseen =p

Varoitus: Et ole kirjautunut sisään. Et voi vastata.

Ketjujonottaja

Merkkijonometodeja
voi myös ketjuttaa. Tämä perustuu siihen, että merkkijonometodi
palauttaa
muutetun kopion
merkkijonosta
. Kun riviä siis suoritetaan,
metodikutsun
tilalle voidaan ajatella sen palauttama
arvo
. Merkkijonometodi ei siis koskaan muuta alkuperäistä merkkijonoa. Tähän on syynä se, että merkkijono on
muuntumaton
(immutable)
tietotyyppi
, minkä merkitys avautuu paremmin kun kohtaamme ensimmäisen tietotyypin, joka on muuntuva. Selvennykseksi katsotaan animaatiota, josta ilmenee miten merkkijonometodit,
muuttujat
ja niiden arvot reagoivat keskenään.
Samasta animaatiosta nähdään myös, että
metodikutsun
paikalla on pölyn laskeuduttua
merkkijono
. Yksi aika yleinen
metodi
, jota käytetään usein ketjutettuna, on strip. Otetaan esimerkkiskenaario, jossa käyttäjän antamaan lukuun halutaan lisätä etunollia, jotta kaikki syötetyt luvut ovat tasan neljän merkin mittaisia. Metodi, jolla etunollia voidaan lisätä, on zfill. Sille annetaan
argumentiksi
pituus, joka merkkijonolla tulee (vähintään) olla etunollien lisäämisen jälkeen. Esimerkkikoodi näyttäisi siis tältä:
luku = input("Anna numero (1-9999): ")
luku = luku.zfill(4)
print(luku)
Normaalisti tämä toimii aivan hyvin:
Anna numero (1-9999): 42
0042
Entä jos käyttäjä sisällyttää vahingossa syötteeseen välilyöntejä ennen ja/tai jälkeen numeron kirjoittamisen? (Tässä tapauksessa molemmat, voit maalata tekstin nähdäksesi numeron jälkeisen tyhjän)
Anna numero (1-9999):  42
 42
Juuri tähän sopii strip-
metodi
. Se poistaa oletuksena kaikki tyhjät merkit
merkkijonon
alusta ja lopusta. Tyhjiä merkkejä ovat välilyöntien lisäksi rivinvaihdot ja sarkainmerkit. Sille voidaan haluttaessa määrittää myös jokin muu merkki poistettavaksi, mutta tyhjien poisto on ehdottomasti yleisin käyttötapa. Metodilla on myös sukulaiset lstrip ja rstrip, jotka toimivat vastaavasti, mutta poistaen merkit vain alusta (lstrip, left) tai lopusta (rstrip, right). Tätä metodia tulee käyttää ennen zfill-metodia, joten koodi muuttuu seuraavanlaiseksi:
luku = input("Anna numero (1-9999): ")
luku = luku.strip().zfill(4)
print(luku)
Rivin eteneminen on purettu auki seuraavassa animaatiossa, käyttäen luku-
muuttujalle
arvoa
" 42 ":

Parempi Meta-if

Jatketaan assistenttien elämään tutustumista. Tarkistimista tulisi myös aika lailla valitusta, jos ne hylkäisivät sellaisen ehtolauseen, jossa on kyllä kaksoispiste lopussa, mutta ylimääräinen välilyönti tai rivinvaihto kaksoispisteen jälkeen... tämäkin siis pitäisi huomioida. Ei ole helppoa assareiden elämä.
Opittavat asiat: Useita
metodikutsuja
käyttävän
ehtolauseen
kirjoittaminen.
Metodien
etsintää dokumentaatiosta harrastetaan jälleen, koska se on tärkeää.

Alustus:
Muutetaan tarkistettavaa siten, että siellä on vähän turhia välejä perässä ja vielä rivinvaihtokin.
In [1]: rivi = "if elain == \"aasi\":   \n "

Haettu vastaus:
Vastauksen tulee edelleen olla ehtolause, joka selvittää päättyykö rivi kaksoispisteeseen. Matkassa on kuitenkin sellainen uusi mutka, että rivin lopussa olevista tyhjistä (sen enempää välilyönneistä kuin rivinvaihdoistakaan) ei saa välittää. Eli nyt pitäisi saada ehtolause toteutumaan myös yllä annetulla uudella testirivillä.
Kirjoita koodirivi alle.
Varoitus: Et ole kirjautunut sisään. Et voi vastata.
Palataan viimein asiaan, mistä koko
metodihullutus
sai alkunsa. Ongelmanamme oli siis, että "pituus" ja "Pituus" ovat eri asioita Pythonin mielestä. Etsimme jo metodin, jolla tämän ongelman voi ratkaista: lower, joka muuttaa
merkkijonosta
kaikki merkit pieniksi. Löysimme myös metodin, jolla syötteestä saa tyhjät vahinkomerkit pois. Luonnollisesti
metodikutsuja
voi ketjuttaa myös tavallisten
funktiokutsujen
perään, jotka palauttavat merkkijonon. Yksi sellainen
funktio
on input. Ratkaistaan ongelma tekemällä input-rivistä seuraavanlainen:
valinta = input("Tee valintasi: ").strip().lower()
Koska käyttäjän
syötteellä
ei ole muuta käyttöä ohjelmassa, tämä on paras paikka muokata sitä. Tällöin käsittelyä ei tarvitse tehdä useassa kohtaa eikä myöskään tarvita erillistä riviä sitä varten. Koko
pääohjelma
näyttää nyt siis tältä:
print("Tämä ohjelma muuntaa yhdysvaltalaisia yksiköitä SI-yksiköiksi")
print("Mahdolliset toiminnot:")
print("pituus")
print("massa")
print("tilavuus")
print("lämpötila")
print()
valinta = input("Tee valintasi: ").strip().lower()
if valinta == "pituus":
    pituus()
elif valinta == "massa":
    massa()
elif valinta == "tilavuus":
    tilavuus()
elif valinta == "lämpötila":
    lampotila()
else:
    print("Valitsemaasi toimintoa ei ole olemassa")

Operaatio logiikka

Jos rehellisiä ollaan, kokonaisten sanojen kirjoittaminen valikossa navigoinnin vuoksi ei ole ihmisten hommaa. Nykymaailmassa tietenkin valikoita selataan lähinnä hiirellä tai kosketusnäytöllä, mutta jo silloin kun ohjelmoitiin nuotion äärellä luolissa, osattiin tehdä valikoista inhimillisempiä. Tähän on tyypillisesti ollut kaksi tapaa: joko vaihtoehdot on numeroitu, ja valinta tehdään antamalla numero; tai jokaista vaihtoehtoa esittää jokin kirjain (yleensä valintasanan ensimmäinen, jos mahdollista). Numerointitapa johtaisi jotakuinkin tämän näköiseen
käyttöliitymään
:
Mahdolliset toiminnot:
1 - Pituus
2 - Massa
3 - Tilavuus
4 - Lämpötila

Tee valintasi (1-4):
Vastaavasti kirjainlyhenteiden käytöllä saataisiin jotain tämän näköistä:
Mahdolliset toiminnot:
(P)ituus
(M)assa
(T)ilavuus
(L)ämpötila

Tee valintasi:
Suluissa oleva kirjain siis kertoo millä merkillä toiminnon voi valita. Valitsemme tällä kertaa jälkimmäisen tavan. Tämä aiheuttaa muutoksia tietenkin ohjeiden tulostukseen sekä itse
ehtorakenteeseen
, jossa valintoja käsitellään.
Aiomme itse asiassa olla niinkin huomaavaisia käyttäjää kohtaan, että ehtorakenne ymmärtää sekä kirjainlyhenteet että kokonaiset sanat. Kykenemme tähän
loogisten operaattoreiden
avulla. Loogisten operaattorien avulla voidaan yhdistää ja kääntää olemassaolevia
ehtoja
. Niitä on kokonaiset kolme kappaletta: and, not ja or. Ne ovat aivan samanlaisia
operaattoreita
kuin aiemmatkin (esim. + ja >=) mutta sattuvat olemaan sanoja merkkiyhdistelmien sijaan. Useissa kielissä nämäkin operaattorit esitetään merkkiyhdistelmin: &&, ! ja ||, mutta Pythonin käyttämät sanat ovat kuvaavampia. Loogisia operaattoreita käytetään pääasiassa
ehtolauseissa
, kun halutaan yhdistellä useita ehtoja tai esittää jokin ehto käänteisenä.
Kuvalähde 5
Käsitellään ensimmäisenä not-operaattori, joka on looginen negaatio. Ehdottomasti yleisin käyttö tälle operaattorille on tilanne, jossa halutaan tarkistaa, onko käyttäjän antama
syöte
kokonaan tyhjä. Tyhjä
merkkijono
tarkoittaa merkkijonoa, jossa on nolla merkkiä eli sen pituus on nolla. Tyhjiä merkkijonoja ovat siis "" ja '', ei mikään muu. Esimerkiksi " " ei ole tyhjä, koska sen sisältönä on yksi välilyönti, ja sen pituus on yksi. Tyhjää merkkijonoa voisi testata tietysti näin:
if valinta == "":
Toinen vaihtoehto on kuitenkin:
if not valinta:
Tämän rivin toiminta perustuu siihen, että tyhjän merkkijonon
totuusarvo
on epätosi eli False. Looginen negaatio puolestaan kääntää ehdon totuusarvon käänteiseksi: se palauttaa True, jos ehto on epätosi, ja False, jos ehto on tosi. Huomattavaa
operaattorissa
on se, että sillä on vain yksi
operandi
toisin kuin useimmilla operaattoreilla, ja tämä operandi on aina not-operaattorin oikealla puolella. Periaatteessa se siis toimii samalla tavalla kuin miinusmerkki numeron edessä.
Kaksi muuta
loogista operaattoria
, eli and ja or, ovat useammin käytössä, koska niiden avulla voidaan yhdistää kaksi
ehtoa
. Yhdistystapa riippuu valitusta operaattorista: and palauttaa Truen, jos molemmat ehdot ovat tosia; or:lle riittää, että toinen ehdoista on. Meidän tarpeisiimme sopii or-operaattori. Sen toimintaa voi tutkia myös ehtolauseiden ulkopuolella tulkissa:
In [1]: valinta = input("Tee valintasi: ").strip().lower()
Tee valintasi: P
In [2]: valinta == "p" or valinta == "pituus"
Out[2]: True
Jos yllä olisi käytetty or-operaattorin sijaan and-operaattoria, olisi saatu aikaan ehto, joka ei voi koskaan olla tosi. Tämä tietenkin siksi, että
merkkijono
ei voi olla samaan aikaan sekä "p" että "pituus". And-operaattoria käytetäänkin useammin tilanteissa, joissa joko tarkastellaan kahta eri
muuttujaa
samanaikaisesti, tai yhtä muuttujaa kahdella eri tavalla. Esimerkiksi meillä voi olla tarkistus koordinaatistoon liittyvässä tehtävässä, jossa selvitetään onko annettu piste origo:
if x == 0 and y == 0:
Vielä varoituksen sana seuraavan näköisestä rivistä, joka sisältää yleisen virheen:
if valinta == "p" or "pituus":
Luettuna sellaisenaan, englanniksi, tämä rivi voi näyttää järkevältä. Tässä kuitenkin or-operaattorin operandit ovat valinta == "p" ja "pituus". Animaatioesimerkin mukaisesti, jos käyttäjän valinta on "m", ensimmäinen
ehto
on False, koska "m" ei selkeästikään ole "p". Kuitenkin toinen
operandi
, joka on siis pelkästään
merkkijono
"pituus", on arvoltaan aina tosi, koska se ei ole tyhjä merkkijono. Yhtäsuuruuta vertaileva
==-operaattori
jää siis täysin or-operaattorin vasemmalle puolelle, eikä or-operaattorin oikealla puolella sitä ole oikeastaan enää edes olemassa. Toisin sanoen tämä ehto on aina tosi, riippumatta siitä, mitä käyttäjä
syöttää
. Vaikka siis tuntuu tyhmältä kirjoittaa valinta-muuttuja vertailuineen kahdesti riville, mitään oikopolkua ei vielä ole käytössämme.

Käärmeen kieli

Perehdytään taas tekstianalyysin maailmaan. Tällä kertaa etsitään tekstejä, joissa esiintyy sanoista "python" ja "ohjelmointi", molemmat vähintään kerran. Tällä tavalla voitaisiin esimerkiksi etsiä nimenomaan Python-ohjelmoinnista kertovia tekstejä ilman, että joukkoon eksyy myös yleisiä ohjelmointitekstejä saati sitten käärmeitä käsitteleviä.
Opittavat asiat:
Loogisten operaattoreiden
käyttö
ehtolauseissa
.

Alustus:
Otetaan taas yksi tarkasteltava
merkkijono
malliksi:
In [1]: teksti = """ 
   ...: asiahan on niin, että ohjelmointi on hauskaa kunnes javascript tapahtuu -  
   ...: sen jälkeen putoat loputtomaan tyhjyyteen 
   ...: """      

Haettu vastaus:
Taas haetaan kokonaista ehtolausetta. Lauseen pitäisi toteutua mikäli sekä "python" että "ohjelmointi" esiintyvät tekstissä vähintään kerran. Esimerkkinä annettu pätkä ei siis toteuta ehtoa! Esiintymien laskennassa voit käyttää tuttua count-
metodia
apuna. Sovitaan, että koko teksti on muutettu pieniksi kirjaimiksi, joten isojen ja pienten kirjainten ongelmasta ei tarvitse välittää. Käytä teksti-muuttujaa vastauksessasi.
Vastaukseen tulee pelkkä if-rivi.
Varoitus: Et ole kirjautunut sisään. Et voi vastata.
Tällä hetkellä
pääohjelma
näyttää siis tältä:
print("Tämä ohjelma muuntaa yhdysvaltalaisia yksiköitä SI-yksiköiksi")
print("Mahdolliset toiminnot:")
print("(P)ituus")
print("(M)assa")
print("(T)ilavuus")
print("(L)ämpotila")
print()
valinta = input("Tee valintasi: ").strip().lower()
if valinta == "p" or valinta == "pituus":
    pituus()
elif valinta == "m" or valinta == "massa":
    massa()
elif valinta == "t" or valinta == "tilavuus":
    tilavuus()
elif valinta == "l" or valinta == "lämpötila":
    lampotila()
else:
    print("Valitsemaasi toimintoa ei ole olemassa")

Pythonin muotoiluakatemia

Otetaan aiemmin edistelty kaaviokuva ohjelman haarautumisesta uudelleen tarkasteltavaksi:
Ohjelman haarautuminen uudestaan. Pääohjelma on ympyröity punaisella, pituus-haara taas sinisellä.
Ohjelman haarautuminen. Punaisella ympyröity on nyt toteutettu. Sinisellä ympyröity toteutetaan seuraavaksi
Kuvassa sinisellä ympyröityä osaa varten meillä onkin jo koodissa
funktion
tynkä valmiina. Tällä
tynkäfunktiolla
testattiin, että
pääohjelman
toteuttama valikko toimii oikein.
def pituus():
    print("Valittiin pituus")
Tästä tynkäfunktiosta olisi nyt tarkoitus tehdä vahva itsenäinen funktio. Funktion pitäisi kysyä käyttäjältä kaksi asiaa: mittayksikkö ja -arvo. Etukäteen päätimme, että mahdolliset yksiköt ovat tuuma, jalka, jaardi ja maili. Näiden kahden asian kysymisen jälkeen funktion pitäisi laskea annettujen tietojen perusteella vastaava arvo SI-järjestelmässä. Tuumat ja jalat voisi muuttaa senteiksi, jaardit metreiksi ja mailit kilometreiksi. Jos yksiköitä vaihdellaan tällä tavalla kyselemättä, on tietenkin kohteliasta ilmoittaa yksikkö myös tuloksessa. Ohjelma voisi edetä esim. näin:
Syötä muutettava yksikkö: tuuma
Syötä muutettava arvo: 12.4
12.40" on 31.50 cm
Aiemman mallin mukainen ratkaisu olisi toteuttaa jälleen viisi
haarainen
ehtorakenne
(4
ehtohaaraa
+ else virheelliselle yksikölle), ja sisällyttää jokaiseen laskutoimenpide sekä tulostus. Tällä kertaa ei tehdä haarojen toiminnallisuudelle erillisiä
funktioita
, vaan sisällytetään ne suoraan pituus-funktion koodiin. Aloitetaan toteutus jälleen tynkäversiolla, josta puuttuu tulostuksesta ohjeet ja ylimääräiset hienostelut. Runko on jo
pääohjelmasta
tuttu. Jokainen muunnos on yleiseltä luonteeltaan arvo * kerroin, ja kertoimet on haettu wikipediasta
def pituus():
    yksikko = input("Anna muutettava yksikkö: ")
    arvo = float(input("Anna muutettava arvo: "))
    if yksikko == "tuuma":
        print(arvo * 2.54)
    elif yksikko == "jalka":
        print(arvo * 30.48)
    elif yksikko == "jaardi":
        print(arvo * 0.9144)
    elif yksikko == "maili":
        print(arvo * 1.609344)
    else:
        print("Valitsemaasi yksikköä ei voida muuntaa")
Tästä päästäänkin varsinaiseen aiheeseen eli lopputuloksen kaunistamiseen.
Oppimistavoitteet: Tässä osiossa opitaan, että jotkut merkit merkkijonoissa ovat ongelmallisia. Toisena asiana tulee kauniiden tulostusten tuottaminen muotoilun avulla. Pyöristyskin saa uusia piirteitä. Tämän osuuden jälkeen hallinnassa ovat perusteet tulostaa vaikka minkälaisia merkkijonoja.

Pakokauhua

Yksikön valinta voi tapahtua sen lyhenteen/merkin pohjalta. Tässä tapauksessa siis in/", ft/', yd ja mi. Lähdetään siis liikkeelle siitä, että vaihdetaan nämä yllä esitettyihin
ehtolauseisiin
. Tuuman ja jalan kohdalla käytetään or-
operaattoria
, jotta molemmat merkintätavat saadaan mukaan.
def pituus():
    yksikko = input("Anna muutettava yksikkö: ")
    arvo = float(input("Anna muutettava arvo: "))
    if yksikko == "in" or yksikko == """:
        print(arvo * 2.54)
    elif yksikko == "ft" or yksikko == "'":
        print(arvo * 30.48)
    elif yksikko == "yd":
        print(arvo * 0.9144)
    elif yksikko == "mi":
        print(arvo * 1.609344)
    else:
        print("Valitsemaasi yksikköä ei voida muuntaa")
Syntaksivärjäys yllä olevassa koodissa näyttää kuitenkin oudolta. Jos "-merkki aloittaa merkkijonon, ja toinen samanlainen lopettaa sen, miten tämä """ pitäisi tulkita? Kooditiedoston suorittaminen antaa
mysteerivirheen
  File "muuntaja.py", line 42
    print("Valitsemaasi toimintoa ei ole olemassa")

^
SyntaxError: EOF while scanning triple-quoted string literal
Triple-quoted string literal tarkoittaa
merkkijonoa
, joka on yksittäisten lainausmerkkien sijaan rajattu kolmella. Eli tosiasiassa myös """-merkintä aloittaa merkkijonon. Koska tiedostosta ei löydy sille paria, Python kohtaa tiedoston lopun ennen sulkevaa """-merkintää, ja heittää SyntaxErrorin auki jääneestä merkkijonosta. """ on siitä erilainen, että toisin kuin yksittäisten lainausmerkkien rajaamat merkkijonot, sen rajaamat merkkijonot saavat jatkua useita rivejä:
runo = """aasisvengaa
dibadii dabadaa
svengaa vaan"""
Hienoa, mutta tämä ei varsinaisesti ratkaise ongelmaa. "-merkin esiintyminen "-merkeillä rajatun merkkijonon sisällä on ongelmallista. Asia voidaan tietenkin väistää vaihtamalla rajausmerkeiksi '. Silloin puolestaan '-merkit merkkijonon sisällä muuttuvat ongelmallisiksi. Joissain merkkijonoissa esiintyy kuitenkin sekä " että '. Mitäs sitten tehdään? Silloin
paetaan
.
Kuvalähde 6
Ei silti nousta koneen ääreltä ja juosta auringonlaskuun, vaan käytetään pakomerkkiä (escape character), joka Pythonissa on \. Tällä merkillä muutetaan
merkkijonossa
esiintyvän merkin tulkintatapaa. Paettu " eli \" tulkitaan merkkijonon rajausmerkin sijaan pelkäksi "-merkiksi. Merkintätapa näyttää tältä:
In [1]: lainausmerkki = "\""
In [2]: print(lainausmerkki)
"
\-merkin jälkeen tuleva merkki (tai merkit joissain tapauksissa) tulkitaan siis poikkeavasti. Lisätään siis kyseinen pätkä koodiimme:
def pituus():
    yksikko = input("Anna muutettava yksikkö: ")
    arvo = float(input("Anna muutettava arvo: "))
    if yksikko == "in" or yksikko == "\"":
        print(arvo * 2.54)
    elif yksikko == "ft" or yksikko == "'":
        print(arvo * 30.48)
    elif yksikko == "yd":
        print(arvo * 0.9144)
    elif yksikko == "mi":
        print(arvo * 1.609344)
    else:
        print("Valitsemaasi yksikköä ei voida muuntaa")
Nyt meillä on kasassa ohjelmakokonaisuus, jolla voi suorittaa pituusmuunnoksia. Siitä kuitenkin puuttuvat vielä kaunistelut, joita laitettiin pääohjelmaan ja joita kaavailtiin suunnitteluvaiheessa.

Kauneuskoulu

Aputulosteet on helppo lisätä
funktion
alkuun, koska ne menevät aikalailla samalla tavalla kuin
pääohjelmassa
.
def pituus():
    print("Valitse pituusyksikkö seuraavien joukosta syöttämällä suluissa annettu lyhenne")
    print("Tuuma (in tai \")")
    print("Jalka (ft tai ')")
    print("Jaardi (yd)")
    print("Maili (mi)")
    print()
    ...
Suurempi haaste on tuottaa
merkkijonoja
, joiden keskellä esiintyy
muuttujien
arvoja
. Puhehan oli, että tulosteet voisivat olla tämän näköisiä:
12.40" on 31.50 cm
Aiemmassa esimerkissä vilahti print-
funktiokutsu
, jolle oli annettu useampi kuin yksi
argumentti
, ja se näytti tältä:
print("Lisätty", nimi, "x", lkm)
Ja tuotti seuraavanlaisen tulosteen, kun nimi- ja lkm-muuttujien arvot olivat "aasi" ja 1:
Lisätty aasi x 1
Print-funktiolle voidaan antaa ennaltamääräämätön määrä argumentteja, ja ne tulostetaan samalla riville välilyönnillä erotettuna. Kuten yllä olevasta esimerkistä näkyy, kukin argumentti voi olla erilainen: tässä osa on
literaaliarvoja
, osa
muuttujia
– ja itse asiassa toisen muuttujan arvo on
merkkijono
ja toisen kokonaisluku. Eli argumenteissa saa olla myös eri
tietotyyppejä
sekaisin. Tästä voidaan ottaa mallia ensimmäiseen kokeiluun tuottaa muuttujia keskelle tulostettavaa riviä:
print(arvo, "\"", "on", arvo * 2.54, "cm")
Tulkissa
kokeilemalla
In [1]: arvo = 12.4
In [2]: print(arvo, "\"", "on", arvo * 2.54, "cm")
12.4 " on 31.496000000000002 cm
Lopputulos eroaa hieman halutusta: "-merkki ei ole kiinni numerossa, 12.4:stä puuttuu toinen desimaali (0 haluttiin näkyviin) ja lopputulosta ei ole pyöristetty. Kokeillaan ensin ratkaista selkeästi suurin ongelma, eli pyöristyksen puute, lisäämällä print-riville round-
funktiokutsu
2 desimaalin tarkkuuteen:
In [1]: arvo = 12.4
In [2]: print(arvo, "\"", "on", round(arvo * 2.54, 2), "cm")
12.4 " on 31.5 cm
Nyt lopputuloksesta jää niin ikään puuttumaan lopusta 0. Lisäksi rivimme alkaa olla hyvin epämääräisen näköinen, ja siitä on vaikea sanoa, miltä lopputulos tulee näyttämään. Yleisesti ottaen on suotuisampaa käyttää erityisiä muotoilutyökaluja yhtään monimutkaisempien
merkkijonojen
rakentamiseen. Pythonissa nämä työkalut kiteytyvät merkkijonojen muotoiluun. Muotoillessa varsinainen merkkijono, johon halutat arvot sijoitetaan, on eräänlainen sapluuna, johon on määritelty erikseen paikat sekä mahdolliset lisämääreet siihen sijoitettaville
arvoille
. Samanhenkisesti siis kuin peruskoulun tehtävät, joissa lauseiden osia piti täydentää viivoille:
_____" on _____ cm
Alaviivoja ei sentään käytetä, mutta vastaavaa merkintää kylläkin. Perusmalli muotoiltavasti merkkijonosta näyttää nimittäin tältä:
"{}\" on {} cm"
Koodissa aaltosulkumerkit osoittavat paikan, johon sijoitetaan jotain muuta. Sijoittaminen tapahtuu muuttamalla merkkijono
f(ormat)-merkkijonoksi
, ja laittamalla halutut muuttujat tai lausekkeet aaltosulkeiden sisälle. Alla olevassa esimerkissä on käytetty uutta muuttujaa tulos, johon pyöristetty tulos on sijoitettu. Periaatteessa koko lausekkeen voisi kirjoittaa aaltosulkujen sisäänkin, mutta sellaista sotkua ei kukaan halua katsoa - varsinkin kun pyöristämiseen on parempi tapa, joka esitellään myöhemmin.
f"{arvo}\" on {tulos} cm"
Kokonaisena tulkkiesimerkkinä:
In [1]: arvo = 12.4
In [2]: tulos = round(arvo * 2.54, 2)
In [2]: print(f"{arvo}\" on {tulos} cm")
12.4" on 31.5 cm
Tässä esimerkissä tulostettavan merkkijonon rakenteesta näkee jo selkeästi paremmin miltä lopputulos tulee näyttämään. Huomattavaa on myös, että moniargumenttisen printin pakottama välilyönti kunkin argumentin väliin voitiin nyt välttää.

Mitäs täältä löytyy?

Tässä tehtävässä meillä on työn alla kuvitteellinen muistipeli. Toteutamme tosin vain rivin, joka kertoo pelaajalle, mitä valitusta ruudusta löytyy. Ruutu on valittu antamalla x- ja y-koordinaatit, ja ruudun sisältöä kuvaa jokin sana. Mikähän sana se voisi olla tässä esimerkissä...?
Opittavat asiast:
Merkkijonojen
muotoilu käyttäen muotoilumerkkijonoja ja
paikanpitimiä
.

Alustus:
Sijoitetaan
alkuun muutama
muuttuja
, että voit käyttää niitä tulostusrivin kirjoituksessa:
In [1]: x = 5
In [2]: y = 6
In [3]: asia = "aasi"

Haettu vastaus:
Kirjoita rivi, joka tulostaa tekstejä muodossa "Ruudusta (5, 6) löytyy aasi". Käytä edellä esitettyä
muotoilumerkkijonoa
(f). Kirjoita siis sapluunamerkkijono, jossa on kolme
paikkaa
muuttujille, ja laita muuttujat niiden sisälle. Muotoiltu merkkijono pitäisi vielä tulostaa. Kaikki tämä yhdellä koodirivillä kuten aina ennenkin.

Varoitus: Et ole kirjautunut sisään. Et voi vastata.
Päivitetään myös oma koodimme käyttämään muotoilumerkkijonoja:
def pituus():
    print("Valitse pituusyksikkö seuraavien joukosta syöttämällä suluissa annettu lyhenne")
    print("Tuuma (in tai \")")
    print("Jalka (ft tai ')")
    print("Jaardi (yd)")
    print("Maili (mi)")
    print()
    yksikko = input("Anna muutettava yksikkö: ")
    arvo = float(input("Anna muutettava arvo: "))
    if yksikko == "in" or yksikko == "\"":
        si_arvo = round(arvo * 2.54, 2)
        print(f"{arvo}\" on {si_arvo} cm")
    elif yksikko == "ft" or yksikko == "'":
        si_arvo = round(arvo * 30.48)
        print(f"{arvo}' on {si_arvo} cm")
    elif yksikko == "yd":
        si_arvo = round(arvo * 0.9144)
        print(f"{arvo} yd on {si_arvo} m")
    elif yksikko == "mi":
        si_arvo = round(arvo * 1.609344)
        print(f"{arvo} mi on {si_arvo} km")
    else:
        print("Valitsemaasi yksikköä ei voida muuntaa")

Aaltojen muotoilijat

Sen lisäksi, että aaltosulkujen sisällä voidaan määrittää mitä muuttujia sijoitetaan mihin kohtaan
merkkijonoa
, myös niiden ulkoasua voi määritellä. Tässä materiaalissa on tästä kaksi erillistä esimerkkiä, joista molemmilla on omat käyttönsä jo peruskurssilla. Toinen on etunollien lisääminen kokonaislukuihin ja toinen
liukulukujen
desimaalien lukumäärän määrittäminen. Etunollien lisäämistä käytetään mm. päivämäärien ja kellonaikojen käsittelyssä. Tyypillinen tietokoneella käsiteltävä aikaleima onkin tämän näköinen: "2015-06-29 09:46:06". Etunollien lisääminen tapahtuu seuraavan näköisellä määrittelyllä:
In [1]: tunnit = 9
In [2]: f"{tunnit:02}"
Out[2]: '09'
Paikanpitimen
ulkoasun määrittelyosa erotetaan nimeämisosasta kaksoispisteellä. Kaksoispistettä seuraava 0 merkitsee, että halutaan lisätä etunollia, kun taas sitä seuraava 2 kertoo, että lopputuloksen pitäisi olla vähintään 2 merkkiä pitkä - eli etunollia lisätään, jos sijoitettava arvo on lyhyempi kuin 2 merkkiä.
Etunollia yleisempi tapaus on kuitenkin desimaalien lukumäärän määrittäminen. Muotoilu tehdään hyvin samanlaisella syntaksilla:
In [1]: pituus = 4.451
In [2]: f"{pituus:.2f}"
Out[2]: '4.45'
Jossa nyt siis .-merkki kertoo, että sitä seuraava numero määrittelee näytettävien desimaalien lukumäärän. Muotoilun lopussa oleva f puolestaan kertoo, että lukua tulee käsitellä liukulukuna. Sen pois jättäminen aiheuttaa
poikkeuksen
jos argumentti on kokonaisluku:
ValueError: Precision not allowed in integer format specifier
Kokonaisluvuille ei puolestaan voida määrittää näyttötarkkuutta, koska niillä ei ole desimaaleja. Näyttötarkkuuden määrittely tarkoittaa, että luvun loppuun lisätään tarvittaessa nollia:
In [1]: pituus = 4
In [2]: f"{pituus:.2f}"
Out[2]: '4.00'
Samalla katoaa tarve käyttää round-
funktiota
, koska pyöristys hoituu muotoilun ohessa. Näinpä siis saamme nätimmän näköisiä rivejä ohjelmaamme:
si_arvo = arvo * 2.54
print(f"{arvo:.2f}\" on {si_arvo:.2f} cm")
Koko funktio näyttää siis tässä vaiheessa tältä. Kertolaskun voisi kirjoittaa myös aaltosulkeiden sisään, mutta pitkät lausekkeet keskellä merkkijonoa alkaavat haittaa luettavuutta.
def pituus():
    print("Valitse pituusyksikkö seuraavien joukosta syöttämällä suluissa annettu lyhenne")
    print("Tuuma (in tai \")")
    print("Jalka (ft tai ')")
    print("Jaardi (yd)")
    print("Maili (mi)")
    print()
    yksikko = input("Anna muutettava yksikkö: ")
    arvo = float(input("Anna muutettava arvo: "))
    if yksikko == "in" or yksikko == "\"":
        si_arvo = arvo * 2.54
        print(f"{arvo:.2f}\" on {si_arvo:.2f} cm")
    elif yksikko == "ft" or yksikko == "'":
        si_arvo = arvo * 30.48
        print(f"{arvo:.2f}' on {si_arvo:.2f} cm")
    elif yksikko == "yd":
        si_arvo = arvo * 0.9144
        print(f"{arvo:.2f} yd on {si_arvo:.2f} m")
    elif yksikko == "mi":
        si_arvo = arvo * 1.609344
        print(f"{arvo:.2f} mi on {si_arvo:.2f} km")
    else:
        print("Valitsemaasi yksikköä ei voida muuntaa")
Esimerkkisuoritus näyttää, miten kaunis tulos saatiin aikaan:
Tämä ohjelma muuntaa yhdysvaltalaisia yksiköitä SI-yksiköiksi
Mahdolliset toiminnot:
(P)ituus
(M)assa
(T)ilavuus
(L)ämpotila

Tee valintasi: p
Valitse pituusyksikkö seuraavien joukosta syöttämällä suluissa annettu lyhenne
Tuuma (in tai ")
Jalka (ft tai ')
Jaardi (yd)
Maili (mi)

Anna muutettava yksikkö: mi
Anna muutettava arvo: 65
65.00 mi on 104.61 km
Muotoilulla
f-merkkijonojen
avulla on todella paljon mahdollisuuksia. Niiden runsautta voi tutkia Pythonin dokumentaatiosta.

Paljonko kello on?

Otsikon kysymys alkaa olla ajankohtainen, kun tätä materiaalia on kahlattu ns. hetki läpi. Onneksi tästäkin asiasta voi tehdä harjoitustehtävän. Tällä kertaa harjoitellaan kellonaikojen esittämistä etunollien kanssa.
Opittavat asiat: Vielä kerran
merkkijonojen
muotoilua. Tällä kertaa opimme miten
paikanpitimiin
voidaan liittää lisämääreitä kuten etunollia.

Alustus:
Kellonaika voidaan tallentaa tehtävää varten kahteen
muuttujaan
:
In [1]: tunnit = 3
In [2]: minuutit = 15

Haettu vastaus:
Jälleen kerran kirjoitamme koodirivin, joka tulostaa muotoillun merkkijonon käyttäen edellä määriteltyjä muuttujia. Sekä tunnit että minuutit tulee tulostaa kahdella numerolla - tarvitaan siis etunolla mikäli luku on pienempi kuin 10. Esimerkin muuttujat tulostuisivat siis merkkijonoksi "Kello on 03:15". Todennäköisesti juuri käsiteltyjä muotoilukonsteja tarvitaan.
Laita vastauksesi laatikkoon
Varoitus: Et ole kirjautunut sisään. Et voi vastata.

Langat yhteen

Hyviä uutisia: kaksi kolmesta puuttuvasta
funktiosta
ovat täysin identtisiä pituus-funktion kanssa. Ainoastaan yksiköt ja laskukaavat muuttuvat funktiosta toiseen. Niinpä voimme ilman kummempia jaaritteluja kirjoittaa nämä funktiot. Massan tapauksessa yksiköitä oli vain kaksi: unssi (oz) ja pauna (lb).
    print("Valitse painoyksikkö seuraavien joukosta syöttämällä suluissa annettu lyhenne")
    print("Unssi (oz)")
    print("Pauna (lb)")
    print()
    arvo = float(input("Anna muutettava arvo: "))
    yksikko = input("Anna muutettava yksikkö: ")
    if yksikko == "oz":
        si_arvo = arvo * 28.349523125
        print(f"{arvo:.2f} oz on {si_arvo:.2f} g")
    elif yksikko == "lb":
        si_arvo = arvo * 0.45359237
        print(f"{arvo:.2f} lb on {si_arvo:.2f} kg")
    else:
        print("Valitsemaasi yksikköä ei voida muuntaa")
Ja tilavuuden tapauksessa:
    print("Valitse nestetilavuusyksikkö seuraavien joukosta syöttämällä suluissa annettu lyhenne")
    print("Kupillinen (cp)")
    print("Pintti (pt)")
    print("Varttigallona (qt)")
    print("Gallona (gal)")
    print()
    yksikko = input("Anna muutettava yksikkö: ")
    arvo = float(input("Anna muutettava arvo: "))
    if yksikko == "cp":
        si_arvo = arvo * 2.365882365
        print(f"{arvo:.2f} cp on {si_arvo:.2f} dl")
    elif yksikko == "pt":
        si_arvo = arvo * 4.73176473
        print(f"{arvo:.2f} pt on {si_arvo:.2f} dl")
    elif yksikko == "qt":
        si_arvo = arvo * 0.946352946
        print(f"{arvo:.2f} qt on {si_arvo:.2f} l")
    elif yksikko == "gal":
        si_arvo = arvo * 3.785411784
        print(f"{arvo:.2f} gal on {si_arvo:.2f} l")
    else:
        print("Valitsemaasi yksikköä ei voida muuntaa")
Lämpötilan tapauksessa on vain yksi yksikkö, jota muuntaa, joten yksikköä ei tarvitse kysyä erikseen. Funktiosta tuleekin siis huomattavasti lyhyempi. Fahrenheitin muunnoskaava Celsius-asteiksi on ihan omanlaistaan taidetta eikä suju suoraan kertolaskulla kuten muut muunnokset. Tätä kaavaa varsinkaan ei kannata yrittää tunkea aaltosulkeiden sisään. Funktio näyttäisi siis tältä:
def lampotila():
    print("Lämpötilamuunnos Fahrenheit-asteista Celsius-asteiksi")
    fahrenheit = float(input("Anna lämpötila: "))
    celsius = (5 / 9) * (fahrenheit - 32)
    print(f"{fahrenheit:.2f} °F on {celsius:.2f} °C")
Määrittämämme neljän funktion (pituus, massa, tilavuus, lampotila) sisällä olevat koodit olisi voinut kirjoittaa myös sellaisenaan
pääohjelmaan
itse
funktiokutsun
paikalle. Ohjelma toimisi täsmälleen samalla tavalla.
Ehtorakenteessa
olisi vain kaksi sisennystasoa. Tavallaan siinä on nytkin, mutta tämä tosiasia on piilotettu osittamalla ohjelma funktioihin. Jos funktiorakenne purettaisiin ja kaikki olisi pääohjelmassa, se olisi tämän näköinen:
print("Tämä ohjelma muuntaa yhdysvaltalaisia yksiköitä SI-yksiköiksi")
print("Mahdolliset toiminnot:")
print("(P)ituus")
print("(M)assa")
print("(T)ilavuus")
print("(L)ämpotila")
print()
valinta = input("Tee valintasi: ").strip().lower()
if valinta == "p" or valinta == "pituus":
    print("Valitse pituusyksikkö seuraavien joukosta syöttämällä suluissa annettu lyhenne")
    print("Tuuma (in tai \")")
    print("Jalka (ft tai ')")
    print("Jaardi (yd)")
    print("Maili (mi)")
    print()
    yksikko = input("Anna muutettava yksikkö: ")
    arvo = float(input("Anna muutettava arvo: "))
    if yksikko == "in" or yksikko == "\"":
        si_arvo = arvo * 2.54
        print(f"{arvo:.2f}\" on {si_arvo:.2f} cm")
    elif yksikko == "ft" or yksikko == "'":
        si_arvo = arvo * 30.48
        print(f"{arvo:.2f}' on {si_arvo:.2f} cm")
    elif yksikko == "yd":
        si_arvo = arvo * 0.9144
        print(f"{arvo:.2f} yd on {si_arvo:.2f} m")
    elif yksikko == "mi":
        si_arvo = arvo * 1.609344
        print(f"{arvo:.2f} mi on {si_arvo:.2f} km")
    else:
        print("Valitsemaasi yksikköä ei voida muuntaa")
elif valinta == "m" or valinta == "massa":
    print("Valitse painoyksikkö seuraavien joukosta syöttämällä suluissa annettu lyhenne")
    print("Unssi (oz)")
    print("Pauna (lb)")
    print()
    yksikko = input("Anna muutettava yksikkö: ")
    arvo = float(input("Anna muutettava arvo: "))
    if yksikko == "oz":
        si_arvo = arvo * 28.349523125
        print(f"{arvo:.2f} oz on {si_arvo:.2f} g")
    elif yksikko == "lb":
        si_arvo = arvo * 0.45359237
        print(f"{arvo:.2f} lb on {si_arvo:.2f} kg")
    else:
        print("Valitsemaasi yksikköä ei voida muuntaa")
 elif valinta == "t" or valinta == "tilavuus":
    print("Valitse nestetilavuusyksikkö seuraavien joukosta syöttämällä suluissa annettu lyhenne")
    print("Kupillinen (cp)")
    print("Pintti (pt)")
    print("Varttigallona (qt)")
    print("Gallona (gal)")
    print()
    yksikko = input("Anna muutettava yksikkö: ")
    arvo = float(input("Anna muutettava arvo: "))
    if yksikko == "cp":
        si_arvo = arvo * 2.365882365
        print(f"{arvo:.2f} cp on {si_arvo:.2f} dl")
    elif yksikko == "pt":
        si_arvo = arvo * 4.73176473
        print(f"{arvo:.2f} pt on {si_arvo:.2f} dl")
    elif yksikko == "qt":
        si_arvo = arvo * 0.946352946
        print(f"{arvo:.2f} qt on {si_arvo:.2f} l")
    elif yksikko == "gal":
        si_arvo = arvo * 3.785411784
        print(f"{arvo:.2f} gal on {si_arvo:.2f} l")
    else:
        print("Valitsemaasi yksikköä ei voida muuntaa")
elif valinta == "l" or valinta == "lämpötila":
    print("Lämpötilamuunnos Fahrenheit-asteista Celsius-asteiksi")
    fahrenheit = float(input("Anna lämpötila: "))
    celsius = (5 / 9) * (fahrenheit - 32)
    print(f"{fahrenheit:.2f} °F on {celsius:.2f} °C")
else:
    print("Valitsemaasi toimintoa ei ole olemassa")
Tästä hirveästä litaniasta on huomattavasti hankalampaa hahmottaa ohjelman toiminta. Esimerkiksi tuo uloin
ehtorakenne
muuttuu niin pitkäksi, että rakenteen aloittava if ja lopettava else eivät mahdu samalle ruudulle. Jos tätä vertaa vielä funktioita käyttävään
pääohjelmaan
, pitäisi olla aika selkeää mitä etua on jo selkeyden kannalta siitä, että ohjelma jaetaan hallittaviin osiin.
print("Tämä ohjelma muuntaa yhdysvaltalaisia yksiköitä SI-yksiköiksi")
print("Mahdolliset toiminnot:")
print("(P)ituus")
print("(M)assa")
print("(T)ilavuus")
print("(L)ämpotila")
print()
valinta = input("Tee valintasi: ").strip().lower()
if valinta == "p" or valinta == "pituus":
    pituus()
elif valinta == "m" or valinta == "massa":
    massa()
elif valinta == "t" or valinta == "tilavuus":
    tilavuus()
elif valinta == "l" or valinta == "lämpötila":
    lampotila()
else:
    print("Valitsemaasi toimintoa ei ole olemassa")
Alla ohjelma joka ollaan tähän asti saatu aikaan
muunnin.py

Sana kirjoista

Jos tarkastellaan yksikkömuuntimen
ehtorakenteita
, niissä havaitaan kohtalaisen selkeä kaava: ehtorakenteella valitaan pääasiassa pelkkä kerroin. Mitä jos kerroin voitaisiin valita jollain toisella tavalla? Tällöin nykyinen ehtorakenteeksi tehty muunnos voitaisiin kirjoittaa pelkkänä kaavana arvo * kerroin. Mutta mistä kerroin sitten saadaan? Voitaisiin tietenkin valita kerroin ehtorakenteessa ennen kaavaa, tyyliin:
def pituus():
    yksikko = input("Anna muutettava yksikkö: ")
    arvo = float(input("Anna muutettava arvo: "))
    if yksikko == "in" or yksikko == "\"":
        kerroin = 2.54 / 100
    elif yksikko == "ft" or yksikko == "'":
        kerroin = 30.48 / 100
    elif yksikko == "yd":
        kerroin = 0.9144
    elif yksikko == "mi":
        kerroin = 1.609344 * 1000
    else:
        print("Valitsemaasi yksikköä ei voida muuntaa")
        return
    print(f"{arvo:.3f} {yksikko} on {arvo * kerroin:.3f} m")
Tässä ei tosin ole kauheasti eroa, paitsi nyt kaikki muutetaan metreiksi joka täytyy huomioida kertoimissa. Asiaa voi kuitenkin parantaa ottamalla käyttöön uuden hyödyllisen tietotyypin.
Osaamistavoitteet: Tämän osion jälkeen tiedät mitä ovat Pythonin sanakirjat ja pari erilaista skenaariota joissa niillä saadaan tehtyä nätin näköistä ja dynaamista koodia (ts. koodia, jonka toimintaa on helppo säätää ilman suuria muutoksia). Lisäksi pureudumme muuntuvien ja muuntumattomien arvojen mysteereihin, joiden kautta viimein selviää miksi on ollut tärkeää teroittaa, että muuttuja on pelkästään viittaus arvoon.

Kaaoksen sanakirja

Sanakirja on
tietorakenne
. Tietorakenteet yleisesti ovat
tietotyyppejä
, jotka sisältävät muita
arvoja
. Yleensä on myös suotavaa, että arvot liittyvät toisiinsa jollain tapaa. Tietorakenteilla on myös oma sisäänrakennettu tapa jolla niihin sijoitetaan arvoja sekä haetaan siellä olevia arvoja. Nimelleen omaisesti sanakirjassa tämä tapahtuu hakusanojen avulla. Näitä hakusanoja kutsutaan
avaimiksi
ja ne voivat olla mitä tahansa muuntumattomia tietotyyppejä - kuitenkin melkein aina ne ovat
merkkijonoja
.
Kuvalähde 7
Sanakirjassa - kuten oikeassakin sanakirjassa - jokainen hakusana eli avain on sidottu johonkin arvoon. Tätä yhteyttä kutsutaan avain-arvo-pariksi. Siinä missä avain on tyypillisesti merkkijono, arvot voivat olla mitä tahansa - myös
tietorakenteita
!. Vastaavasti sama avain ei voi esiintyä sanakirjassa kuin kerran, mutta arvojen puolella tätä rajoitusta ei ole. Hakeminen on yksisuuntaista, eli ainoastaan avaimella voi löytää arvon - ei toisinpäin.
Sanakirja merkitään aaltosulkeilla. Kaikki aaltosulkeiden välissä oleva tulkitaan
sanakirjan
määrittelyksi. Määrittelyn
syntaksi
vaatii, että avain-arvo-parit erotetaan toisistaan pilkuilla, ja
avain
erotetaan
arvosta
kaksoispisteellä. Koska sanakirjan määrittely sisältää tyypillisesti paljon merkkejä, se usein jaetaan monelle riville - määrittely voidaan katkoa sekä aaltosulkeiden että pilkkujen kohdalta rivinvaihdoilla. Alla oleva esimerkki saattaa vinkata mihin suuntaan tämä ajatus sanakirjojen käytöstä on kehittymässä:
pituuskertoimet = {
    "in": 0.0254,
    "ft": 0.3048,
    "yd": 0.9144,
    "mi": 1609.344
}
Esimerkistä nähdään myös, että aaltosulkeiden välissä oleva määrittely on selkeyden nimissä hyvä
sisentää
yhden tason syvemmälle. Aeimmasta poiketen tällä sisennyksellä ei ole koodin suorituksen kannalta mitään vaikutusta, mutta selkeyden kannalta paljonkin - nyt nähdään helposti missä kohdin sanakirjan määrittely loppuu ilman, että tarvii etsiä missä aaltosulut päättyvät.

Inventaariohakuteos

Yksi aika selkeä käyttötarkoitus sanakirjalla voisi olla (hyvin) yksinkertaisen inventaarion ylläpito. Tehdään siis sellainen...
Opittavat asiat:
Sanakirjan
määrittely.

Haettu vastaus:
Kirjoita uuden sanakirjan määrittely. Sanakirjassa tulee olla seuraavat
avaimet
ja arvot:
  • "aasi": 15
  • "sininen ämpäri": 0
  • "epätoivoinen mursu": 1
Sanakirjan määrittely kannattaa jakaa usealle riville. Sanakirjan nimen voit valita ihan itse, kunhan se on laillinen Python-nimi. Kirjoita avain-arvoparit määrittelyssä yllä annetussa järjestyksessä.

Varoitus: Et ole kirjautunut sisään. Et voi vastata.

Avaimet käteen

Äsken mainostettiin, että
sanakirjasta
saa
arvon
avainta
vastaan. Ihan hyvä, mutta miten? Tähän on kaksi tapaa:
tietorakenteisiin
liittyvä yleinen hakusyntaksi sekä sanakirjojen oma get-
metodi
. Hakusyntaksi toimii siten, että tietorakenteen (tai, useimmiten, sellaisen sisältävän muuttujan) perään laitetaan hakasulut, joiden sisällä annetaan hakuarvo - tässä tapauksessa siis avain. Eli jos vaikka aiemmin määritetystä sanakirjasta halutaan jaardin kerroin ulos:
In [1]: pituuskertoimet = {
    ...:     "in": 0.0254,
    ...:     "ft": 0.3048,
    ...:     "yd": 0.9144,
    ...:     "mi": 1609.344
    ...: }
   ...:
In [2]: kerroin = pituuskertoimet["yd"]
In [3]: kerroin
Out[3]: 0.9144
Haku sanakirjasta
palauttaa
avainta vastaavan arvon, joten se voidaan
napata kiinni
muuttujaan. Se voidaan myös hakea suoraan laskukaavaan, joten miten olisi hetki sitten esitellyn
ehtorakenteen
korvaaminen
sanakirjalla
ja avainhaulla...? Avainkin voidaan nimittäin hakea muuttujasta:
In [4]: yksikko = "ft"
In [5]: print(pituuskertoimet[yksikko])
0.3048
Ja näitä tietoja soveltava uutuudenkiiltävä pituus-funktio:
def pituus():
    pituuskertoimet = {
        "in": 0.0254,
        "ft": 0.3048,
        "yd": 0.9144,
        "mi": 1609.344
    }
    yksikko = input("Anna muutettava yksikkö: ")
    arvo = float(input("Anna muutettava arvo: "))
    kerroin = pituuskertoimet[yksikko]
    print(f"{arvo:.3f} {yksikko} on {arvo * kerroin:.3f} m")
Tässä koodissa on tosin kaksi (uutta) puutetta: tuumaa ja jalkaa ei voi hakea symbolilla; ja mitä tapahtuu jos käyttäjä valitsee yksikön jota ei tueta?. Ensimmäinen on helppo ratkaista. Kuten todettua, sama arvo voi löytyä useammalla avaimella, joten ne voidaan vain lisätä sanakirjaan:
    pituuskertoimet = {
        "in": 0.0254,
        "\"": 0.0254,
        "ft": 0.3048,
        "'": 0.3048,
        "yd": 0.9144,
        "mi": 1609.344
    }
Tässä näkyy samalla aiemmin mainostettu dynaamisuus:
funktion
toiminta muuttuu pelkästään tätä
sanakirjaa muokkaamalla siinä missä ehtolauseratkaisussa uuden yksikön lisääminen vaatisi
ehtorakenteen
muokkaamista.

Sherlock de Bug ja sopimaton avain

Tyypillisesti Python antaa poikkeuksen, jos koodissa yritetään tavoitella jotain olematonta. Niin käy silloinkin kun sanakirjasta yritetään hakea arvoa avaimella, jota ei ole määritelty.
Opittavat asiat: Mitä tapahtuu, kun
sanakirjasta
koitetaan hakea
avainta
jota siellä ei ole.

Alustus:
Tarvitaan jokin sanakirja, joten luodaan sellainen suoraan materiaaliesimerkistä:
In [1]: pituuskertoimet = {
   ...:     "in": 0.0254,
   ...:     "ft": 0.3048,
   ...:     "yd": 0.9144,
   ...:     "mi": 1609.344
   ...: }

Haettu vastaus:
Tehtävänä on siis yrittää hakea sanakirjasta arvoa avaimella, jota siellä ei ole. Kuten ennenkin tämän tyyppisissä tehtävissä, vastaukseen halutaan
poikkeuksen
nimi.

Varoitus: Et ole kirjautunut sisään. Et voi vastata.
Kun tiedetään, että olemattoman avaimen käytöstä seuraa
poikkeus
, luonteva askel on tietenkin käsitellä se. Jos siis käyttäjän antama
syöte
aiheuttaa poikkeuksen, tulostetaan virheviesti. Tällä hetkellä try-
haaraan
sisään tulee laittaa tuo print-hirviölauseke kokonaisuudessaan. Vaikka lause onkin pitkä, se ei itse asiassa ole kauhean räjähdysherkkä. Riskiä ottaa vahingossa kiinni jokin muu poikkeus kuin se jota ajateltiin ei siis ole.
def pituus():
    pituuskertoimet = {
        "in": 0.0254,
        "\"": 0.0254,
        "ft": 0.3048,
        "'": 0.3048,
        "yd": 0.9144,
        "mi": 1609.344
    }
    yksikko = input("Anna muutettava yksikkö: ")
    arvo = float(input("Anna muutettava arvo: "))
    try:
        kerroin = pituuskertoimet[yksikko]
    except EdellisenTehtävänVastaus:
        print("Valitsemaasi yksikköä ei voida muuntaa")
    else:
        print(f"{arvo:.3f} {yksikko} on {arvo * kerroin:.3f} m")
Virheellisen arvon syöttäminen jätetään edelleen käsittelemättä - se tehdään kunnolla seuraavassa materiaalissa. Oikeastaan
sanakirjan
määrittely funktion sisällä ei myöskään ole järkevää, koska sitä käytetään pelkästään
arvojen
lukemiseen. Tällä hetkellä se luodaan uudestaan aina kun
funktiota
kutsutaan. Se kanattaakin siis siirtää
vakioksi
ohjelman alkuun (mahdollisten import-lauseiden alle). Eli näin, tuomatta vielä muuta ohjelmaa mukaan kuvioon.
PITUUSKERTOIMET = {
    "in": 0.0254,
    "\"": 0.0254,
    "ft": 0.3048,
    "'": 0.3048,
    "yd": 0.9144,
    "mi": 1609.344
}

def pituus():
    yksikko = input("Anna muutettava yksikkö: ")
    arvo = float(input("Anna muutettava arvo: "))
    try:
        kerroin = PITUUSKERTOIMET[yksikko]
    except EdellisenTehtävänVastaus:
        print("Valitsemaasi yksikköä ei voida muuntaa")
    else:
        print(f"{arvo:.3f} {yksikko} on {arvo * kerroin:.3f} m")
Saman voi tehdä muillekin funktioille. Siirrytään kuitenkin asiassa eteenpäin sanakirjojen muokkaamiseen.

Sanakirjat vasaran alla

Tietorakenteille
on usein myös ominaista se, että niitä voidaan muokata.
Sanakirjaan
voidaankin ohjelman suorituksen aikana sekä lisätä uusia
avain
-
arvo
-pareja. Avaimiin liitettyjä arvoja voi myös vaihtaa toisiin. Tehdään tätä tarkoitusta varten uusi funktio. Kuvitellaan ohjelma, joka käsittelee mittaustuloksia erinäisistä lähteistä. Yksi tulos on määritelty sanakirjana, jossa on kaksi avain-arvo-paria: mittayksikkö ja numeerinen arvo, esim:
mittaus = {
    "yksikko": "in",
    "arvo": 4.64
}
Jatkokäsittelyn kannalta olisi parempi, että kaikki mittaukset olisivat samaa yksikköä. Äsken kasailtua pituus-
funktiota
ja sitä varten luotua kertoimet-sanakirjaa voidaan hieman muokkaamalla käyttää tähän tarkoitukseen. Tarkoitus on siis, että uusi funktio saa käsiteltäväksi yhden mittauksen, jonka se muuttaa metreiksi. Funktion määrittelyriviin tulee nyt siis parametri. Samalla arvon ja yksikön
kysyminen
poistuu ohjelmasta, ja ne otetaan suoraan mittaus-sanakirjasta edellisessä osiossa opitulla avainhaulla. Myös arvo metreinä voidaan laskea suoraan
muuttujaan
.
def muunna_metreiksi(mittaus):
    arvo = mittaus["arvo"]
    yksikko = mittaus["yksikko"]
    metrit = arvo * PITUUSKERTOIMET[yksikko]
Kysymykseksi jääkin enää: miten uudet arvot saadaan laitettua sanakirjaan. Menetelmä on itse asiassa hyvin pitkälti sama kuin avainhaussa. Tiedetään, että
muuttujan
rooli vaihtuu, jos se on
sijoitusoperaattorin
vasemmalle puolella: siihen sijoitetaan sen sijaan, että haettaisiin sen arvo. Avainhaun
syntaksi
toimii samalla tavalla: jos se on sijoitusoperaattorin vasemmalle puolella, avaimeen liitetty arvo korvataan uudella:
In [1]: mittaus = {
   ...:     "yksikko": "in",
   ...:     "arvo": 4.64
   ...: }
In [2]: mittaus["yksikko"] = "m"
In [3]: mittaus
Out[3]: {'yksikko': 'm', 'arvo': 4.64}
Toiminta näyttää yksinkertaiselta, ja onkin sitä, mutta tähän liittyy yksi täysin uusi asia:
sanakirja
on
muuntuva
. Huomaa, että missään vaiheessa muokkausta ei luoda uutta
muuttujaa
- sanakirjan muokkaus kohdistuu suoraan alkuperäiseen sanakirjaan. Aiemmin tässä materiaalissa teroitettiin, että
merkkijono
on
muuntumaton
tietotyyppi ja kaikki
metodit
jotka "muuttavat" merkkijonoa tosiasiassa tekevät siitä muutetun kopion. Periaatteessa mittausyksikön muokkaus voitaisiin tehdä nimittän myös merkkijonoilla ja replacella:
In [1]: mittaus = "12.984 in"
In [2]: mittaus.replace("in", "m")
Out[2]: '12.984 m'
In [3]: mittaus
Out[3]: '12.984 in'
In [4]: mittaus = mittaus.replace("in", "m")
In [5]: mittaus
Out[5]: '12.984 m'
Esimerkissä ensin yritetään "muokata" mittausta, mutta mittaus-muuttujan sisältöä katsottaessa huomataan, että replacen vaikutus ei jäänyt voimaan. Vasta neljäs rivi saa aikaan muutoksen, mutta kyseessä ei ole enää sama mittaus-muuttuja vaan uusi jolla sattuu olemaan sama nimi. Sanakirjat - kuten muutkin muuntuvat tietotyypit - toimiva juuri päinvastoin: pääasiassa niitä muokkaavat operaatiot sörkkivät suoraan alkuperäistä. Sanakirjojen kanssa onkin hyvä pitää mielessä, että se itsessään on vain kokoelma
viittauksia
arvoihin
. Arvot itsessään eivät siis ole varsinaisesti sanakirjan sisällä, vaan ovat olemassa muistissa ihan samalla tavalla kuin muutkin arvot. Se, että ne ovat sanakirjan
avaimia
vastaavia arvoja ei siis tee niistä millään tapaa erikoisia.

Madonreikä sanakirjassa

Kolikon toinen puoli muuntumattomien arvojen kohdalla on se mystisempi. Tässä tehtävässä tutkitaan miten muuttujasta toiseen sijoitettu sanakirja käyttäytyy. Esimerkissä käsitellään jälleen inventaariojuttuja. Kuvitteellisessa funktiossa muokataan inventaarion tilaa, mutta historiasyistä halutaan myös jättää alkuperäinen tilanne talteen.
Opittavat asiat: Nähdä miten
muuntuvat
tietotyypit eroavat
muuntumattomista
.

Alustus:
Sanakirjaa
tarvitaan jälleen, joten luodaan sellainen. Luodaan myös uusi
muuttuja
, johon
sijoitetaan
sama sanakirja sekä lisätään uusi-muuttujassa olevan sanakirjan "lkm"-
avaimen
arvoon 5.
In [1]: esine = {
   ...:     "nimike": "aasi",
   ...:     "lkm": 10
   ...: }
In [2]: uusi = esine
In [3]: uusi["lkm"] += 5

Haettu vastaus:
Tutki tämän operaation jälkeen molempia sanakirjoja ja kirjoita alla olevaan laatikkoon mikä on esine-muuttujassa olevan sanakirjan "lkm"-avaimen arvo.

Varoitus: Et ole kirjautunut sisään. Et voi vastata.
Jos jossain tapauksessa oikeasti haluat kaksi samanlaista
sanakirjaa
, tämä onnistuu sanakirjan copy-
metodilla
. Se palauttaa nimensä mukaisesti kopion sanakirjasta. Me haluamme kuitenkin tehdä funktion, joka nimenomaan muokkaa alkuperäistä mittausta. Niinpä siis muokkaus tapahtuu näin:
def muunna_metreiksi(mittaus):
    arvo = mittaus["arvo"]
    yksikko = mittaus["yksikko"]
    metrit = arvo * PITUUSKERTOIMET[yksikko]
    mittaus["yksikko"] = "m"
    mittaus["arvo"] = metrit
Puuttuuko tästä jotain (muuta kuin poikkeusten käsittely)?
Funktiossa
ei ole lainkaan return-lausetta, mikä vaikuttaa kovasti epäilyttävältä... Funktio on kuitenkin ihan oikein. Koska
muuntuvan
tietorakenteen muokkaus vaikuttaa suoraan muistissa olevaan dataan, muutos heijastuu kaikkialle missä tätä sanakirjaa käsitellään. Osoitetaan tämä lisäämällä yksinkertainen
pääohjelma
. Huomaa miten tulostus tehdään kahdesti täsmälleen samanlaisella rivillä, ja kuinka muunna_metreiksi-
funktiokutsun
paluuarvoa
ei oteta ollenkaan talteen.
PITUUSKERTOIMET = {
    "in": 0.0254,
    "\"": 0.0254,
    "ft": 0.3048,
    "'": 0.3048,
    "yd": 0.9144,
    "mi": 1609.344
}

def muunna_metreiksi(mittaus):
    arvo = mittaus["arvo"]
    yksikko = mittaus["yksikko"]
    metrit = arvo * PITUUSKERTOIMET[yksikko]
    mittaus["yksikko"] = "m"
    mittaus["arvo"] = metrit

mittaus = {"yksikko": "in", "arvo": 4.64}
print(f"{mittaus['arvo']:.3f} {mittaus['yksikko']}")
print("on")
muunna_metreiksi(mittaus)
print(f"{mittaus['arvo']:.3f} {mittaus['yksikko']}")
Suoritetaan ja nähdään että:
4.64 in
on
0.118 m
Entä se
poikkeusten
käsittely? Edelleen on mahdollista, että funktiolle tulee
sanakirja
, jossa on pituusyksikkö, jota ei osata muuntaa (edelleen oletetaan, että arvot ovat aina liukulukuja). Tehdään käsittely siten, että opitaan samalla jotain uutta. Jos mittausta ei voida muuntaa, lisätään siihen uusi avain-arvo-pari: avain "virheellinen", joka on
totuusarvo
ja tarkemmin True, jos mittausta ei voitu muuntaa. Avaimen lisääminen tapahtuu samalla tavalla kuin olemassaolevan avaimen arvon muuttaminen - eli tämäkin toimii kuten
muuttujien
kanssa. Poikkeuksen löytäminen tapahtuu laittamalla avainhaku try-
haaraan
, virhetapaus except-haaraan ja sanakirjan päivitys else-haaraan:
def muunna_metreiksi(mittaus):
    arvo = mittaus["arvo"]
    yksikko = mittaus["yksikko"]
    try:
        metrit = arvo * PITUUSKERTOIMET[yksikko]
    except KeyError:
        mittaus["virheellinen"] = True
    else:
        mittaus["yksikko"] = "m"
        mittaus["arvo"] = metrit
Se uusi juttu, joka opitaan, on itse asiassa se, että on olemassa toinenkin tapa hakea sanakirjasta
arvoja
avaimella
: get-
metodi
. Metodin käyttö eroaa avainhausta siten, että se ei aiheuta
poikkeusta
, jos avainta ei löydy. Poikkeuksen sijaan get-metodi
palauttaa
ennaltamäärätyn oletusarvon, joka on None, jos toisin ei ole osoitettu.
In [1]: mittaus_1 = {"yksikko": "m", "arvo": 1.0}
In [2]: mittaus_2 = {"yksikko": "aasi", "arvo": 3.63, "virheellinen": True}
In [3]: mittaus_1.get("virheellinen", False)
Out[3]: False
In [4]: mittaus_2.get("virheellinen", False)
Out[4]: True
Virheellisen mittauksen käsittely voidaan nyt lisätä pääohjelmaan demonstraatiomielessä:
PITUUSKERTOIMET = {
    "in": 0.0254,
    "\"": 0.0254,
    "ft": 0.3048,
    "'": 0.3048,
    "yd": 0.9144,
    "mi": 1609.344
}

def muunna_metreiksi(mittaus):
    arvo = mittaus["arvo"]
    yksikko = mittaus["yksikko"]
    try:
        metrit = arvo * PITUUSKERTOIMET[yksikko]
    except KeyError:
        mittaus["virheellinen"] = True
    else:
        mittaus["yksikko"] = "m"
        mittaus["arvo"] = metrit

mittaus = {"yksikko": "aasi", "arvo": 4.64}
print(f"{mittaus['arvo']:.3f} {mittaus['yksikko']}")
print("on")
muunna_metreiksi(mittaus)
if not mittaus.get("virheellinen", False):
    print(f"{mittaus['arvo']:.3f} {mittaus['yksikko']}")
else:
    print("Virheellinen yksikkö")
Jolloin
4.64 aasi
on
Virheellinen yksikkö
Tämä pääohjelma on aika teennäinen esimerkki. Oikea käyttökonteksti tehdylle muunnosfunktiolle vaatii kuitenkin seuraavan materiaalin opiskelua. Tässä on kuitenkin opittu perusteet
sanakirjoista
sekä siitä mitä
muuntuva arvo
tarkoittaa.

Loppusanat

Tässä materiaalissa olemme omaksuneet kolme keskeistä ohjelmointitaitoa: tekstin käsittely ja tuottaminen
merkkijonoilla
;
poikkeustilanteiden
käsittely try-except-rakenteilla; ja keskeisimpänä ohjelman haarautumisen hallinta
ehtorakenteilla
. Ohjelmamme muuttuivat yksinkertaisesta yhden operaation tekevistä laskurutiineista sellaisiksi, joissa käyttäjä voi oikeasti tehdä valintoja ja saada sen perusteella erilaisia lopputuloksia. Havaitsimme myös, että ohjelmoinnissa yksi suuri osa työstä on se, että ns. insinööriratkaisu muutetaan sellaiseksi, että sitä uskaltaa näyttää kenelle tahansa. Toimiva logiikka onkin vain yksi osa ohjelmaa – myös käyttökokemus on tärkeä.
Materialissa tutustuttiin myös
tietorakenteisiin
ja niiden joukosta tarkemmin
sanakirjoihin
. Koska sanakirja on materiaalissa esiintyvistä
tietotyypeistä
ensimmäinen
muuntuva
, käsittelimme myös sitä miten muuntuvat tietotyypit eroavat ratkaisevasti
muuntumattomista
, kuten esimerkiksi
merkkijonoista
.
Huomioitavaa tässä materiaalissa on, että jätimme jälkimmäisessä esimerkissä käyttäjän
syötteet
tarkistamatta, jotta koodilistaukset pysyisivät paremmin luettavina. Kaikkialla, missä käyttäjältä pyydetään lukuja, pitäisi tietenkin olla try-except-rakenne, jotta ohjelma käyttäytyy nätisti. Tutustumme ensi materiaalissa siihen, miten tämä voidaan hoitaa ilman, että koodiin tulee lisää lukemista vaikeuttavaa sisältöä. Toinen ilmeinen puute yksikkömuunninohjelmassa on se, että yhden muunnoksen jälkeen ohjelma pitää käynnistää uudestaan. Myös tähän epäkohtaan pääsemme jatkossa tarttumaan.

Kuvalähteet

  1. alkuperäinen lisenssi: CC-BY 2.0
  2. alkuperäinen lisenssi: CC-BY-NC 2.0 (teksti lisätty)
  3. alkuperäinen lisenssi: CC-BY 2.0 (teksti lisätty)
  4. alkuperäinen lisenssi: CC-BY 2.0 (teksti lisätty)
  5. alkuperäinen lisenssi: CC-BY 2.0
  6. alkuperäinen lisenssi: CC-BY 2.0 (kuvaa rajattu)
  7. alkuperäinen lisenssi: CC-BY 2.0 (teksti lisätty)
?
  1. Kuvaus
  2. Esimerkit
Absoluuttinen polku (absolute path) on käyttöjärjestelmäkäsite, joka kertoo hakemiston "koko osoitteen". Absoluuttinen polku ilmaistaan levyaseman juuresta lähtien joten se ei ole riippuvainen siitä mikä on aktiivinen hakemisto. Absoluuttisia polkuja pyritään yleensä välttämään koodissa, erityisesti jos tarkoitus on tehdä koodia jota joku muukin saattaa käyttää. Toinen käyttäjä ei välttämättä sijoita tiedostoja juuri täsmälleen samanlaiseen hakemistorakenteeseen kuin olet omalla koneellasi tehnyt. Erityisesti jos tiedostosi yleensä asuvat kotihakemistossa, pelkästään absoluuttisessa polussa oleva eri käyttäjänimi sotkee kaiken jonkun muun koneella.
Ajonaikaisesta (engl. run time) puhuttaessa määreenä on se aikaväli, kun ohjelma on käynnissä. Esimerkiksi Pythonissa virheet (syntaksivirheitä lukuun ottamatta) tarkastetaan ajonaikaisesti. Ohjelma saattaa siis olla käynnissä ja toimia tiettyyn pisteeseen saakka, kunnes törmätään käsittelemättömään poikkeukseen – ajonaikaiseen virheeseen (engl. run time error).
  1. Kuvaus
  2. Esimerkit
Alkio (engl. item, element) on listan tai muun tietorakenteen sisältämä yksittäinen arvo. Useimmiten alkioista puhutaan juuri listojen yhteydessä. Tällöin alkiolla on arvon lisäksi paikka eli indeksi, joka kertoo sen sijainnin listassa etäisyytenä listan alusta. Niinpä siis listan ensimmäisen alkion indeksi on 0.
  1. Kuvaus
  2. Esimerkit
Alustamisella (engl. initialize) tarkoitetaan yleisesti jonkin arvon asettamista muuttujalle muuttujan luonnin yhteydessä. Pythonissa ei ole mahdollista luoda muuttujaa, jolla ei ole myös jotain arvoa. Niinpä tyypillisesti käytetäänkin sanamuotoa ”muuttuja alustetaan arvolla x”, millä tarkoitetaan sitä, että muuttuja, joka luodaan, saa luomisen yhteydessä (eikä vasta joskus myöhemmin) arvon x.
  1. Kuvaus
  2. Esimerkit
Argumentti (engl. argument) on funktiokutsussa käytettävä arvo, joka välitetään kutsuttavalle funktiolle. Funktiokutsun alkaessa argumentit sijoitetaan parametreiksi kutsuttuihin muuttujiin, joiden kautta arvoihin pääsee funktion sisällä käsiksi.
Arvo (engl. value) on konkreettista, tietokoneen muistissa sijaitsevaa tietoa, jota käytetään ohjelman suorituksen aikana. Arvoilla on tyyppi ja sisältö; esimerkiksi numero 5 on tyypiltään kokonaisluku, jonka sisältö on 5. Useimmiten arvot liitetään muuttujiin, mutta myös operaatioiden ja funktiokutsujen paluuarvot sekä koodissa sellaisenaan esiintyvät arvot ovat arvoja. Käytännössä siis kaikkea konkreettista mitä ohjelma käsittelee voidaan kutsua arvoiksi.
  1. Kuvaus
  2. Esimerkit
Avain (engl. key) on ikään kuin sanakirjan ”indeksi”, eli sillä valitaan yksittäinen arvo tietorakenteen sisältä. Kutakin avainta vastaa yksi arvo. Avaimina käytetään yleensä merkkijonoja, mutta ne voivat olla mitä tahansa muuntumattomia tietotyyppejä, kuten lukuja tai monikkoja.
  1. Kuvaus
  2. Kurssin avainsanat
Avainsanat (engl. keyword) ovat ohjelmointikielessä kielen käyttöön varattuja sanoja, joilla on erityinen merkitys. Hyvät tekstieditorit tyypillisesti merkitsevät avainsanat muista nimistä eroavalla tavalla (esimerkiksi lihavoinnilla tai tietyllä värillä). Avainsanat ovat yleensä suojattuja, eli samannimisiä muuttujia ei voi luoda. Yleisiä avainsanoja Pythonissa ovat esimerkiksi funktioihin liittyvät def ja return. Avainsanat ovat siis osa ohjelmointikielen kielioppia.
  1. Kuvaus
  2. Esimerkit
Avainsana-argumentti-termiä (engl. keyword argument, lyh. kwarg) käytetään, kun funktio- tai metodikutsussa argumentteja annetaan sijoittamalla niitä parametrien nimiin. Tätä käytetään erityisesti format-metodin yhteydessä: "Hei {nimi}".format(nimi="hemuli"). Toinen yleinen käyttötapaus on silloin, kun kutsutulla funktiolla on paljon valinnaisia argumentteja ja näistä vain osa halutaan määrittää. Avainsana-argumentin käyttö voi myös selkeyttää koodia, erityisesti sellaisten argumenttien kohdalla joille annetaan arvoksi True tai False.
  1. Kuvaus
  2. Esimerkit
Avausmoodilla kerrotaan Pythonille (ja käyttöjärjestelmälle) millä tavalla tiedosto avataan. Tiedosto voidaan avata lukemista tai kirjoittamista varten. Oletuksena, eli jos avausmoodia ei erikseen määritellä, tiedosto avataan lukumoodissa ("r"). Kirjoitusmoodeja on kaksi:
  • "w", eli write, joka kirjoittaa tiedoston sisällön puhtaalta pöydältä hävittäen mahdollisesti aiemmin olemassa olleen saman nimisen tiedoston.
  • "a", eli append puolestaan kirjoittaa olemassaolevan tiedoston loppuun.
Molemmat kirjoitusmoodit luovat tiedoston, jos sitä ei vielä ole olemassa.
Siinä missä UNIX-pohjaiset järjestelmät tuottavat \n-merkkejä rivinvaihdoiksi, Windows tuottaa \r\n-rivinvaihtoja, joissa r on carriage return -merkki. Se on kirjoituskoneiden peruja ja tarkoittaa toimenpidettä, jossa kirjoituspää siirretään takaisin rivin alkuun. Yleisesti ottaen tämä on lähinnä asia, joka on hyvä tietää – Python käsittelee molempia rivinvaihtoja kiltisti.
Data (engl. data) on ohjelmoinnin asiayhteydessä mitä vaan tietoa, joka ei kuitenkaan yleisesti kata itse ohjelmakoodia. Yleensä datasta puhuttaessa tarkoitetaan yksittäisiä literaaliarvoja, muuttujien sisältämää tietoa tai jostain tietolähteestä (kuten tiedostosta tai verkko-osoitteesta) luettua tai sinne kirjoitettua tietoa. Nyrkkisääntönä voi kuitenkin pitää sitä, että koodi ja data ovat eri asioita, ja koodi käsittelee dataa. (Joissain yhteyksissä koodikin lasketaan dataksi, mutta näihin ei tällä kurssilla syvennytä.)
Debuggaus (engl. debugging) tarkoittaa ohjelmointivirheiden – bugien – jäljittämistä ja korjaamista. Bugien jäljille pääsemiseen on monia eri tapoja, joista ehkä hyödyllisimpänä Python tarjoaa ohjelman kaatumisen yhteydessä näytettävät virheviestit. Myös debug-printit ovat tavanomainen keino virheiden paikantamiseen; kyseessä on print-komentojen ripottelu koodiin väliaikaisesti esimerkiksi sen selvittämiseen, mihin asti koodin suoritus pääsee, tai muuttujien arvojen tutkimiseen ajonaikaisesti. Debuggaus on niin oleellinen osa ohjelmointia, että sitä varten on kehitetty myös erikseen työkaluja, joita kutsutaan debuggereiksi. Debuggereihin emme kuitenkaan tällä kurssilla koske.
Pythonissa dokumenttimerkkijono (engl. docstring) on kommentin kaltainen merkintä, mutta sillä on oma erityistarkoituksensa. Dokumenttimerkkijono merkitään yleensä kolmella lainausmerkillä (eli '''dokumentti''' tai """dokumentti""". Jos dokumenttimerkkijono on sijoitettu funktion def-rivin alapuolelle (sisennettynä), siitä tulee funktion dokumentaatio, jonka saa esiin help-funktiolla tulkissa. Samoin kooditiedoston alkuun sijoitettu dokumenttimerkkijono muuttuu moduuliin dokumentaatioksi. Dokumenttimerkkijonossa on hyvä kertoa funktion toimintaperiaate sekä selittää mitä sen parametrit ja paluuarvot ovat.
Dokumenttimerkkijonoja ei tule käyttää kommenttien sijasta! Muualla kuin edellä mainituissa paikoissa kommentointiin tulee käyttää kommentteja (eli #-merkillä alkavia rivejä)
  1. Kuvaus
  2. Esimerkit
Ehto-nimitystä (engl. condition) käytetään tällä kurssilla ehtolauseiden ja while-silmukoiden siitä osasta, joka määrittelee milloin lause on tosi ja milloin epätosi. Ehtoa on siis kaikki joka on ehtolauseen aloittavan avainsanan (if tai elif) ja sen päättävän kaksoispisteen välissä.
  1. Kuvaus
  2. Esimerkit
Ehtolause (engl. conditional statement) on yksittäisen ehdon määrittelevä rivi koodissa, jota seuraa sisennetty koodilohko, joka määrittää miten ehdon toteutuessa tulee toimia. Varsinaisia ehtolauseita ovat if- ja elif-lauseet, joista jälkimmäinen ei voi esiintyä ilman ensimmäistä. Toisiinsa liitetyt ehtolauseet muodostavat ehtorakenteita. Ehtolause päättyy aina kaksoispisteeseen, ja tämän kaksoispisteen jälkeen on seurattava vähintään yksi sisennetty koodirivi.
  1. Kuvaus
  2. Esimerkit
Ehtorakenne (engl. conditional structure) on yhdestä tai useammasta toisiinsa liitetystä ehtolauseesta muodostuva rakenne, joka haarauttaa ohjelman suoritusta. Useimmissa ehtorakenteissa on vähintään kaksi haaraa: if ja else. Näiden välissä voi olla myös mielivaltainen määrä elif-lauseilla aloitettuja haaroja. On myös mahdollista, että ehtorakenteessa on pelkkä if-lause. Ehtorakenteessa kussakin haarassa on suoritettavaa koodia, joka kuvaa miten ohjelman tulee ehdon määrittelemässä tilanteessa toimia.
Kokonaisuudessaan ehtorakenne käydään läpi siten, että ensin tarkistetaan järjestyksessä ensimmäisen, eli if-lauseen, ehdon paikkansapitävyys. Jos ehto evaluoitui totuusarvoon True, ohjelman suoritus jatkuu kyseisen if-lauseen lohkosta, jonka suorituksen jälkeen siirrytään koko lopun ehtorakenteen ohi. Jos ehto taas evaluoitui Falseksi, käydään järjestyksessä ehtolauseita läpi toistaen samaa kuin ensimmäisen if-lauseen kohdalla, ja jos mikään ehto ei ollut paikkansapitävä, suoritetaan else-lauseen lohko.
Epätosi (engl. false) on toinen kahdesta mahdollisesta totuusarvosta ja toisen, eli toden, vastakohta. Sitä voidaan pitää lopputuloksena loogisissa ja vertailuoperaatorioissa, jotka eivät pidä paikkansa. Esimerkiksi vertailuoperaatio 5 < 4 ei pidä paikkansa, joten kyseinen operaatio evaluoituu epätodeksi. Pythonissa epätotta merkitään avainsanalla False.
  1. Kuvaus
  2. Esimerkit
Erotin (engl. separator) on merkkijonoihin ja tekstitiedostoihin liittyvä termi. Sillä tarkoitetaan tiettyä merkkiä, joiden kohdilta merkkijono on tarkoitus katkaista, kun se luetaan koodiin. Esimerkiksi, jos merkkijono sisältää tietoja, jotka on tarkoitus lukea listaan, erotin erottelee merkkijonon osat alkioiksi. Koodissa käytetään usein merkkijonojen split-metodia näissä tilanteissa – metodilla voidaan siis pätkiä erottimien kohdilta merkkijono listaksi.
Evaluointi (engl. evaluation) tarkoittaa lausekkeen tai muuttujan arvon lopputuloksen määrittämistä. Suoritettaessa lauseet evaluoituvat joksikin tietyksi arvoksi.
Exception on yleisimpien poikkeusten pääluokka. Kutsumme sitä Pokémon-poikkeukseksi, koska jos sitä käyttää try-except-rakenteessa, except ottaa kiinni kaikki poikkeukset. Tämä ei ole hyvä asia, koska se tekee vikatilanteiden tulkitsemisen vaikeammaksi sekä ohjelman käyttäjälle, että koodarille itselleen – se ottaa nimittäin kiinni myös ohjelmointivirheet, jolloin et saa mitään hyödyllistä tietoa ohjelman kaatuessa.
  1. Kuvaus
  2. Esimerkit
  3. Parametrien valinta
Merkkijonojen format-metodi on Pythonissa tehokas tapa sisällyttää muuttujien arvoja tulostettavaan tai tallennettavaan tekstiin. Merkkijonoon määritetään paikanpitimiä (esim: {:.2f}) joihin sijoitetaan format-metodin argumentit. Esimerkki: "Aasin korvien väli on {:.2f} tuumaa".format(mittaus).
Huom: format on vanhentunut tapa yleiseen merkkijonojen muotoiluun, nykyisin käytetään enemmän f-merkkijonoja. Vanhemmalla formatilla on kuitenkin edelleen käyttönsä.
  1. Kuvaus
  2. Esimerkit
Funktio (engl. function) on ohjelmassa oleva itsenäinen kokonaisuus, joka muodostuu määrittelyrivistä (def-lauseella) sekä funktion suoritettavista koodiriveistä. Funktioita käytetään selkeyttämään ohjelman rakennetta sekä koodin toiston välttämiseen. Funktiot kommunikoivat keskenään ja pääohjelman kanssa funktion parametrien sekä paluuarvojen välityksellä. Funktion sisällä määritetyt muuttujat (ml. parametrit) ja muut nimet ovat olemassa ainoastaan funktion sisällä. Vastaavasti funktioiden ei pitäisi lukea arvoja itsensä ulkopuolelta.
  1. Kuvaus
  2. Esimerkit
Funktiokutsu (engl. function call) on menetelmä, jonka seurauksena ohjelman suoritus ”hyppää” toiseen kohtaan koodia – sen funktion alkuun, jota kutsutaan. Funktiota kutsuttaessa sillä annetaan sulkeissa argumentit, joihin funktiolohkon koodista käsin pääsee käsiksi funktiomäärittelyn vastaavista kohdista löytyvien parametrien kautta. Funktion suoritus päättyy, kun törmätään funktion loppuun tai return-lauseeseen. Tällöin ohjelmakoodin suoritus palaa takaisin edelliseen kohtaan, eli sinne, mistä funktiota kutsuttiin, ja funktiokutsu korvautuu funktion paluuarvolla.
Toisin sanoen kutsumalla saadaan yksi ohjelman osa käyttämään toista – esimerkiksi pääohjelma funktiota tai funktio toista funktiota.
  1. Kuvaus
  2. Esimerkit
Funktioiden määrittely tapahtuu def-lauseella, jonka yhteydessä annetaan nimi funktiolle sekä sen parametreille. Kaikkien näiden valinta on oleellinen osa hyvän ja yleiskäyttöisin funktion kirjoittamista. Nimi tulisi valita siten, että se kuvaa mahdollisimman hyvin mitä funktio tekee - vastaavasti parametrien nimien tulisi olla sellaisia, että niistä voi helposti päätellä millaiset argumentit funktiolle pitää antaa. Funktion varsinainen koodi määritetään sisennettynä def-rivin alle. Funktion koodi voi ja usein sisältääkin useita rivejä - se voi myös sisältää muita sisennyksiä (esim. ohjausrakenteita).
  1. Kuvaus
  2. Esimerkit
Generaattori (engl. generator) on erityinen objektityyppi, joka toimii esimerkiksi for-silmukassa listan tavoin. Generaattori ei kuitenkaan ole muistissa oleva kokoelma arvoja, vaan erityinen funktio, joka tuottaa arvoja laiskasti, eli sitä mukaa kuin sitä käydään läpi. Tästä johtuen generaattorin ”sisältöä” ei ole mahdollista tulostaa, eikä siitä voida valita alkioita indeksiosoituksella. Generaattorit eivät kuulu alkeiskurssin aihepiiriin.
  1. Kuvaus
  2. Esimerkit
  3. Tilasanakirjat
Globaali muuttuja (engl. global variable) on pääohjelman tasolla esitelty muuttuja, jota muokataan suoraan funktiossa tuomatta sitä funktion nimiavaruuteen parametrin kautta. Globaalien muuttujien käyttö on huonoa ohjelmointityyliä, ja niiden sijaan tietoa kuuluisikin kuljettaa funktioille argumentteina ja ottaa funktiolta vastaan paluuarvoina muutettuja arvoja. Näin tekemällä välttää niin kutsutun globaalin tilan, joka huonontaa koodin ymmärrettävyyttä.
Globaali näkyvyys koskee kaikkia nimiä (muuttujat, funktiot jne.) jotka on määritelty pääohjelman tasolla. Kaikki nimet jotka kuuluvat globaalin näkyvyyden piiriin ovat luettavissa mistä tahansa ohjelmakoodissa. Niihin ei voi kuitenkaan sijoittaa uusia arvoja muualla kuin pääohjelmatasolla. Yleisesti ottaen globaalilta näkyvyysalueelta tulisi käyttää funktioiden sisällä vain vakioita ja muita funktioita. Varsinaiset pääohjelman muuttujat pitäisi aina siirtää funktioihin parametrien kautta.
Haara (engl. branch) on yksi keskenään vaihtoisista reiteistä, joita pitkin ohjelman suoritus voi tietystä pisteestä lähtien edetä. Esimerkiksi ehtorakenteissa jokainen if-, elif- ja else-lohko haarauttaa ohjelman suorituksen.
  1. Kuvaus
  2. Esimerkit
Hypystä (engl. jump) puhuttaessa tarkoitetaan ohjausrakenteen aiheuttamaa siirtymistä, jonka jälkeen ohjelman suoritus jatkuukin jostain muualta kuin seuraavalta koodiriviltä.
  1. Kuvaus
  2. Esimerkki
Ikuinen silmukka tai ikisilmukka (engl. infinite loop) on silmukka, joka ei pääty ikinä – silmukan alaisuuteen kuuluvaa koodia siis toistetaan ”ikuisesti”. Ikisilmukoilla on ohjelmoinnissa käyttötarkoituksensa, mutta silloin tällöin tahattomasti syntynyt ikisilmukka voi myös olla ohjelman jumiutumisen aiheuttava bugi. Pythonissa ikuiset silmukat onnistuvat pääasiassa while-silmukoilla.
  1. Kuvaus
  2. Esimerkit
Indeksi (engl. index) on kokonaislukuarvo, joka osoittaa alkion sijainnin järjestetyssä tietorakenteessa (lista, monikko, mutta myös merkkijono!). Indeksit alkavat nollasta, joten viimeinen indeksi on (rakenteen pituus - 1). Tätä voi ajatella etäisyytenä rakenteen alusta. Python tuntee myös negatiiviset indeksit, jolloin indeksi -1 viittaa aina viimeiseen alkioon, -2 toiseksi viimeiseen jne. Kun rakenteesta otetaan alkio indeksin perusteella, puhutaan usein osoittamisesta.
  1. Kuvaus
  2. Esimerkit
Kun käytetään tietorakenteen, esimerkiksi listan, indeksiä, puhutaan (indeksi)osoittamisesta. Tämä osoittaminen merkitään hakasuluilla, esim. arvosanat[0]. Indeksiosoitus palauttaa alkion. Osoitus listan ulkopuolelle aiheuttaa IndexError-poikkeuksen, ja on hyvä pitää mielessä että listan viimeinen indeksi on sen pituus - 1 (koska indeksointi alkaa nollasta). Indeksi voi olla myös negatiivinen, jolloin laskenta alkaa listan lopusta (eli -1 on listan viimeinen alkio).
Katso myö: leikkaus.
Jäsenarvo (engl. attribute) on objektille kuuluva arvo, eli ominaisuus eli attribuutti. Se on siis nimi, joka kuuluu objektin sisäiseen nimiavaruuteen, ja siihen päästään käsiksi objektin kautta: aika.tm_hour joka antaisi aika-objektista tunnit.
Kierros (engl. iteration) on toistorakenteiden eli silmukoiden yhteydessä käytetty sana. Kierroksella viitataan siihen, kun silmukan alla sijaitseva koodi suoritetaan kertaalleen alusta loppuun – tämä on siis yksi kierros.
Kirjasto (engl. library) tai moduuli (engl. module) (kuten niitä Pythonissa virallisesti kutsutaan) on valmiiksi kirjoitettua koodia, jolla on oma rajattu tarkoituksensa. Tyypillisesti kirjasto sisältää ainakin nipun aihepiiriinsä kuuluvia funktioita, mutta voi sisältää muutakin (esim. luokkia tai vakioita). Esimerkiksi Turtle on kirjasto, jonka tarkoitus on tarjota helposti käytettäviä piirtofunktioita.
  1. Kuvaus
  2. Materiaaliesimerkki
  3. Peruskäyttö
Komentoriviargumentti (engl. command line argument) tai -parametri on nimitys lisätiedolle, joka annetaan komennon yhteydessä kun ohjemaa käynnistetään komentoriviltä. Komentoriviargumentit erotetaan toisistaan tyypillisesti välilyönnillä. Esimerkiksi komennossa python koodi.py koodi.py on itse asiassa komentoriviargumentti. Komentoriviargumentteja voi käsitellä Python-koodissa sys-moduulin argv-muuttujan kautta.
  1. Kuvaus
  2. Esimerkit
Kommentti (engl. comment) on kooditiedostossa olevaa tekstiä, joka ohitetaan kun koodia suoritetaan. Kussakin kielessä on oma tapansa sille miten rivi merkitään kommentiksi. Pythonissa se on #- eli risuaitamerkki (engl. hash character), jonka jälkeen riviltä löytyvän tekstin Python-tulkki ohittaa kokonaan. Kommenteilla voi selventää koodin lukijalle (tai itselleen) mitä koodissa tapahtuu. Yleensä kommentit on hyvä laittaa omille riveilleen kommentoitavan koodin yläpuolelle.
Ohjelman ja sen funktioiden toiminta kuvataan yleensä mieluiten dokumenttimerkkijonossa. Kommentteja käytetään enemmänkin välihuomioiden tekemiseen.
Toinen tapa käyttää kommentteja on tilapäisesti kommentoida rivejä pois esimerkiksi vaihtoehtoisen koodin testaamiseksi. Tällöin aiempaa koodia ei tarvitse poistaa – kätevää, jos myöhemmin osoittautuu, että sitä tarvitaan sittenkin.
Kooditiedosto (engl. code file) on tekstimuotoinen tiedosto, joka sisältää suoritettavaa koodia. Python-kooditiedosto suoritetaan komentokehotteesta kirjoittamalla python koodi.py, jossa koodi.py on tiedoston nimi. Kooditiedostoa suorittaessa yksittäisten rivien paluuarvot eivät tule näkyviin – ainoastaan print-funktiolla tulostettavat tiedot näkyvät käyttäjälle.
Ohjelman käyttämät arvot ovat kovakoodattuja (engl. hard coded) silloin, kun ne esiintyvät literaaliarvoina – eli semmoisenaan – ohjelman lähdekoodissa sen sijaan, että ne selvitettäisiin ajonaikaisesti esimerkiksi kysymällä käyttäjältä tai lukemalla tiedostosta.
Kutsupyyntö (eng. callback) on erityisesti nykyaikaisessa ohjelmoinnissa yleinen mekanismi, jossa toiselle - usein jonkun muun tekemälle - ohjelman osalle annetaan funktio, jota sen tulee kutsua toimintansa aikana. Jos tavallinen funktiokutsu vastaa puhelinsoittoa, kutsupyyntö on loogisesti soittopyyntö. Jos ohjelman osa käyttää kutsupyyntöä, sen dokumentaatio tyypillisesti kertoo, millaisen funktion sille voi antaa - erityisesti mitä parametreja funktiolla voi olla ja millainen arvo sen tulee palauttaa.
Käsittelijä(funktio) (engl. handler) on funktio, joka on kiinnitetty tapahtumaan siten, että sitä kutsutaan kun tarkkailtu tapahtuma havaitaan. Tämä johtaa siihen, että yleensä käsittelijää ei kutsuta samassa koodissa missä se on määritelty, vaan se toimii takaisinkutsuna. Käsittelijät liittyvät yleensä käyttöliittymä- ja pelikirjastoihin, joissa ohjelman pääsilmukka pyörii kirjaston sisällä ja tarkkailee tapahtumia. Käsittelijät ovat varsinaisen sovelluksen tapa toteuttaa omat toimintonsa tapahtumien kautta. Koska sovelluksen kirjoittaja ei voi vaikuttaa siihen miten käsittelijäfunktiota kutsutaan, sen parametrien ja paluuarvojen tulee vastata kirjaston antamia määrityksiä.
Käyttöliittymä (engl. User Interface, lyh. UI) on rajapinta ohjelman ja ohjelman käyttäjän – tyypillisesti ihmisen – välillä. Yksinkertaisessa tekstipohjaisessa käyttöliittymässä käyttäjältä voidaan pyytää ohjelman suoritusta varten tietoa input-funktiokutsujen avulla. print-funktiolla voidaan puolestaan esittää käyttäjälle tietoa ja lopputuloksia.
Monet loppukäyttäjälle interaktiiviseen käyttöön tarkoitetut ohjelmat toimivat jonkinlaisen graafisen käyttöliittymän (engl. Graphical User Interface, lyh. GUI) kautta. Näihin sisältyy yleensä ikoneita, painikkeita, avattavia valikoita ynnä muita hiirellä tai kosketusnäytöllä tökittäväksi tarkoitettuja käyttöliittymäelementtejä. Tällä kurssilla tutustumme lopputyön yhteydessä pintaa raapaisemalla graafisten käyttöliittymien sielunelämään.
Käyttöliittymäelementti (engl. UI element, widget) on jokin (yleensä graafiselle) käyttöliittymälle ominainen komponentti, jonka kautta käyttäjän vuorovaikutus ohjelman kanssa on mahdollista. Tällaisia ovat esimerkiksi napit, valikot, liukusäätimet ynnä muut.
Lause (engl. statement) on ohjelmointikielessä nimitys yksittäiselle suoritettavalle asialle, joka on yleensä yksi koodirivi.
Lauseke (engl. expression) tarkoittaa ohjelmoinnissa evaluoitavaa yksikköä. Esimerkiksi 5 + 5 ja "aasi" != "apina" ovat lausekkeita, jotka evaluoituvat arvoiksi 10 ja True. Lauseke yksin ei muuta ohjelman tilaa mitenkään, ellei sillä ole sivuvaikutuksia. Sen sijaan lauseke vaikuttaa osana lausetta.
  1. Kuvaus
  2. Esimerkit
Leikkaamisella (engl. slice) tarkoitetaan sitä, kun sekvenssistä (yleensä listasta, mutta myös merkkijonoista) otetaan osasekvenssi. Lopputuloksena on samaa tyyppiä oleva arvo, joka on kopio valitusta alueesta. Valinnassa merkitään aloitus- ja lopetusindeksit. Molemmat ovat tosin valinnaisia. Leikkaus merkitään sivu = kokoelma[5:10] joka ottaisi siis alkiot indekseistä 5…9. Kaksoispisteen jälkeinen luku on ensimmäinen indeksi jota ei oteta mukaan!
Leikkaaminen ei koskaan aiheuta IndexErroria!
  1. Kuvaus
  2. Esimerkit
Lista (engl. list) on järjestetty kokoelma arvoja, joka on Python-ohjelmoinnissa todellinen monitoimikone. Lista voi sisältää mitä tahansa arvoja, eikä sen kokoa tarvitse tuntea ennalta.
Listassa olevia arvoja kutsutaan alkioiksi. Jokaisella alkiolla on listassa paikka, jota kutsutaan indeksiksi. Indeksit alkavat nollasta! Kaiken tämän lisäksi lista on luonteeltaan muuntuva tietotyyppi. Kaikesta tästä on kerrottu hyvin paljon kolmosmateriaalissa.
Lista voi myös sisältää muita listoja. Tällä tavalla muodostettua tietorakennetta kutsutaan kaksiulotteiseksi listaksi (engl. two-dimensional list). Tietenkin sisäkkäisiä listoja (engl. nested list) voi olla kahtakin tasoa syvemmälle, jolloin ulottuvuuksien lukumäärä kasvaa vastaavasti. Tällöin puhutaan moniulotteisista listoista (engl. multidimensional list).
Literaaliarvo (engl. literal) on yleisnimitys arvoille jotka esiintyvät koodissa sellaisenaan. Arvo ei siis ole muuttujassa, vaan se on kirjoitettu koodiin. Esimerkiksi lauseissa x = 5 ja print("aasi"), 5 ja "aasi" ovat literaaliarvoja. Termiä käytetään pääasiassa yksinkertaisten muuttujatyyppien eli lukujen, totuusarvojen ja merkkijonojen kanssa.
  1. Kuvaus
  2. Muunnokset
Liukuluku (engl. floating point number, lyh. float) on tietokoneiden käyttämä desimaaliluvun approksimaatio. Tietokoneet eivät arkkitehtuurinsa vuoksi pysty käsittelemään oikeita desimaalilukuja, joten niiden tilalla käytetään liukulukuja. Liukuluvut saattavat aiheuttaa pyöristysvirheitä - tämä on hyvä pitää mielessä niitä käyttäessä. Pythonissa on olemassa decimal-moduuli, joka pystyy käsittelemään desimaalilukuja tarkasti.
Lohko (engl. block) on nimitys joukolle koodirivejä jotka kuuluvat yhteen. Lohkoa yhdistää se, että rivit ovat samalla sisennystasolla (tosin lohko voi sisältää myös muita lohkoja). Tyypillisiä lohkoja ovat esim. ehtorakenteiden suoritettavat osat, eli ne sisennyt koodirivit jotka seuraavat ehtoa / elseä. Lohko tulkitaan päättyneeksi kun vastaan tulee rivi, jonka sisennystaso on pienempi kuin lohkoon kuuluvien rivien.
  1. Kuvaus
  2. Lisätietoa
Looginen operaattori (engl. boolean operator) viittaa Boolen algebran operaatiohin, joissa käsitellään totuusarvoja. Tyypillisiä loogisia operaatioita ovat ehtolauseista tutut and, not ja or. Näistä and on tosi jos ja vain jos molemmat operandit ovat tosia; or on tosi jos ainakin toinen operandeista on tosi; ja not on tosi, jos sen ainoa operandi on epätosi.
Lähdekoodi – lyhemmin koodi – (engl. source code, code; alan slangi sorsa) tarkoittaa tekstiä, joka on kirjoitettu ohjelmointikielellä.
Merkillä (engl. character) tarkoitetaan ohjelmoinnissa yksittäistä datana esiintyvää kirjainta, numeroa, välimerkkiä tai muuta vastaavaa symbolia. Pythonissa merkki edustaa pienintä merkkijonon yksittäistä palasta.
  1. Kuvaus
  2. Esimerkit
Merkkijono (engl. string) on tietotyyppi, joka sisältää tekstiä. Sitä käytetään erityisesti käyttäjän kanssa viestimiseen. Merkkijonojen sisältöä voidaan myös tallentaa tiedostoihin. Pythonissa merkkijono merkitään lainaus- tai heittomerkillä (esimerkiksi "aasi" tai 'aasi'). Suosimme ensimmäistä. Merkkijono voidaan merkitä myös kolmella merkillä jolloin se voi olla monirivinen – tätä käytetään erityisesti dokumenttimerkkijonojen (docstring) kanssa. Merkkijono on muuntumaton tietotyyppi – kaikki, mikä näennäisesti muokkaa merkkijonoa, tosiasiassa luo (ja palauttaa) siitä muutetun kopion.
  1. Kuvaus
  2. Esimerkit
Metodi (engl. method) on funktio, joka on osa objektia eli objektin ominaisuus, jolla objekti usein muuttaa omaa tilaansa. Metodia kutsuttaessa käsiteltävä objekti tulee kutsun eteen: valinta.lower(). Metodeita kutsutaan myös joskus jäsenfunktioiksi (engl. member function).
Metodikutsu (engl. method call) vastaa toiminnaltaan funktiokutsua. Merkittävänä erona kuitenkin käsiteltävä objekti on metodikutsun edessä siinä missä funktiokutsussa se annettaisiin argumenttina. Metodikutsussa siis objekti tyypillisesti käsittelee itseään. Esimerkiksi sana.upper() on metodikutsu, jossa käsitellään sana-muuttujan viittaamaa objektia.
Moduuli (engl. module) on periaatteessa mikä tahansa Python-kooditiedosto. Yleisemmin kuitenkin moduulista puhutaan kirjaston synonyymina. Tyypillinen moduuli sisältää yhteen asiaan keskittyviä funktioita ja mahdollisesti muutakin (esimerkiksi vakioita ja luokkia). Laajat ohjelmat on usein myös jaettu useisiin moduuleihin siten että kukin moduuli keskittyy ohjelman toiminnan tiettyyn osa-alueeseen.
  1. Kuvaus
  2. Esimerkit
Monikko (engl. tuple) on ns. jäädytetty lista. Se on siis järjestetty kokoelma arvoja kuten listakin, mutta se on muuntumaton objekti - sen sisältöä ei siis voi muuttaa muuten kuin luomalla uuden kopion. Monikkoja voidaan siis ainoastaan luoda uusia ja lukea. Monikko merkitään yleensä kaarisulkeilla: (1, 2, 3), mutta myös pelkkä 1, 2, 3 on monikko.
Toisin kuin lista, monikko voi toimia sanakirjan avaimena.
  1. Kuvaus
  2. Esimerkit
Pythonissa objektit erotellaan muuntuviin ja muuntumattomiin. Muuntumaton (engl. immutable) arvo on sellainen, jonka sisältö ei voi muuttua - kaikki operaatiot jotka näennäisesti muuttavat arvoa tosiasiassa luovat siitä uuden kopion, joka yleensä sijaitsee uudessa muistipaikassa. Esimerkiksi merkkijonot ovat tyypillinen muuntumaton tyyppi Pythonissa. Siksi merkkijonojen kanssa näkee yleensä jotain tällaista: valinta = valinta.lower()
  1. Kuvaus
  2. Esimerkit
Pythonin objekteissa on kahta tyyppiä: muuntuvia ja muuntumattomia. Muuntuvat (engl. mutable) objektit ovat sellaisia, joiden arvo voi muuttua suorituksen aikana esim. metodikutsun seurauksena. Yleisin esimerkki muuntuvista objekteista on lista: muumilaakso.append("Hemuli") muuttaa muumilaakso-nimistä listaa pysyvästi lisäämällä siihen uuden arvon. Kaikki listaan viittaavat lauseet ohjelmassa käsittelevät tästä eteenpäin listaa, johon "Hemuli" on lisätty.
Yksinkertaistettu tapa käsittää muuttuja (engl. variable) on ajatella sitä tietovarastona – muuttuja sisältää jotain. Tätä ilmaisua käytetään usein puheessa, mutta se ei ole täysin tarkka. Tarkempi kuvaus on, että Python-muuttuja on viittaus arvoon. Se on siis yhteys muuttujan nimen ja tietokoneen muistissa olevan arvon välillä. Muuttuja ei siis varsinaisesti sisällä arvoa – se ainoastaan sisältää tiedon siitä mistä arvo löytyy.
Ohjelmointikielissä on oleellista ymmärtää määrittelyn (engl. definition) ero suorittamiseen. Määrittelemällä luodaan kuvauksia funktioista, muuttujista ja erilaisista tietorakenteista – tavallaan siis kerrotaan ohjelmointikieltä käyttäen, minkälainen jokin edellä mainituista asioista on, tai mitä sen kuuluisi tehdä. Pythonissa määrittelyn ja suorittamisen ero on helpoin ymmärtää funktioiden avulla. Funktiomäärittelyssä funktio vasta luodaan – ikään kuin tehtaalla koottu laite. Funktiota varsinaisesti käytetään – eli sen toiminnallisuus hyödynnetään funktiota varten määriteltyä koodia ajamalla – vasta funktiokutsun yhteydessä. Samaa vertausta käyttäen funktiokutsu vastaa siis sitä hetkeä, kun tehtaalta saapunut laite käynnistetään.
  1. Kuvaus
  2. Esimerkit
Nimeämätön vakio tai taikaluku (engl. magic number) on koodissa esiintyvä literaaliarvo, jota ei selitetä millään tavalla. Hyvään ohjelmointityyliin kuuluu taikalukujen välttäminen. Oikea – itsedokumentoiva – tapa on nimetä koodissa esiintyvät vakiot muuttujiin, jolloin niiden muuttaminen onnistuu tarpeen tullen yhdestä paikasta yhdellä muutoksella, ja koodin lukijan on helpompi ymmärtää koodia.
  1. Kuvaus
  2. Nimeämiskäytännöt
Muuttujilla, funktioilla, vakioilla, moduuleilla ja muilla vastaavilla on kullakin nimi (engl. identifier) – se osa lähdekoodia, joka tarkoittaa kyseistä asiaa. Esimerkiksi, jos ohjelmoija määrittelee koodin alussa muuttujan leveys arvolla 15, kyseisellä leveys-nimellä voidaan myöhemmin käyttää kyseistä muuttujaa. Nimen voidaan siis ajatella olevan ohjelmoijan ja koodia lukevan tulkin yhteinen ymmärrys siitä, mihin asioihin lähdekoodissa esiintyvät sanat viittaavat. Nimet kuuluvat aina johonkin nimiavaruuteen.
Nimiavaruus (engl. namespace) on joukko nimiä (muuttujia, vakioita, funktioita jne.) jotka kuuluvat samaan kontekstiin. Esimerkiksi funktion sisällä, eli funktiomääritelmän lohkossa on oma nimiavaruus: funktion sisällä määritetyt nimet ovat käytössä ainoastaan sen sisällä. Ohjelmalla on myös aina päänimiavaruus (engl. global namespace), jossa kaikki pääohjelmassa määritetyt nimet sijaitsevat. Tavallista import-lausetta käytettäessä saadaan niin ikään erillinen nimiavaruus, johon päästään käsiksi moduulin nimen kautta – moduulin sisäiset nimet ovat siis tällöin erillisessä avaruudessa. Katso myös näkyvyysalue.
Nimikonflikti syntyy, jos useammalle kuin yhdelle arvolle koitetaan antaa sama nimi. Tällöin tapahtuu niin, että tuoreempi sijoitus jåä voimaan. Tästä seuraa yleensä ohjelman kaatavia virheitä, koska usein arvot ovat eri tyyppiä. Voi jopa käydä niin, että epämääräisesti nimetyn funktion päälle tallennetaan vahingossa saman niminen muuttuja.
  1. Kuvaus
  2. Esimerkit
Näkyvyysalue (engl. scope) määrittää sen, onko jokin tietty nimi (muuttuja, funktio tms.) käytettävissä tietyssä kohdassa ohjelmaa. Esimerkiksi funktiomääritelmän lohkossa voidaan viitata funktiossa määriteltyihin muuttujiin, koska ne ovat funktion näkyvyysalueella. Sen sijaan muut funktiot eivät voi viitata näihin muuttujiin, koska ne kuuluvat eri näkyvyysalueelle. Globaalin (ts. pääohjelman) näkyvyysalueen nimet ovat luettavissa kaikkialla koodissa.
  1. Kuvaus
  2. Syventävää nippelitietoa
Näppäimistökeskeytyksellä (engl. keyboard interruption) voi pakottaa jumiin jääneen ohjelman sammumaan. Sen saa aikaan painamalla Ctrl+C sen terminaalin ollessa auki, jossa ohjelma pyörii. Pythonissa näppäimistökeskeytyksen saa käsiteltyä kaappaamalla KeyboardInterrupt-poikkeuksen try-except-rakenteella.
Objekti (engl. object), joskus myös olio, on Pythonissa yleistä terminologiaa. Kutsumme objekteja pääasiassa arvoiksi alkeiskurssilla, mutta Pythonissa kaikkea voi käsitellä objekteina. Tämä tarkoittaa, että mihin tahansa voidaan viitata muuttujilla (esimerkiksi funktion voi sijoittaa muuttujaan). Tämän kurssin puitteissa objekti-termiä käytetään sellaisista arvoista joilla on metodeja.
Objektit nousevat merkittävämpään rooliin alkeista eteenpäin, erityisesti koodissa jossa käytetään luokkia.
Ohjausrakenne (engl. control structure) on yleisnimitys ohjelmointikielen sallimista keinoista, jotka hallitsevat jollain tavalla ohjelman suorituksen kulkua. Näihin rakenteisiin lukeutuvat kurssin puitteissa ehtorakenteet, toistorakenteet sekä poikkeusten käsittely.
Ohjelmointiongelma on ohjelmointityön kohde. Se on siis jokin todettu tarve, jota varten ohjelmaa koodataan. Tarve voi olla jonkin tietokoneella tehtävän asian automatisointi, verkkosivun pystyttäminen tai ihan vain hauskan pelin tekeminen.
Ohjelmointityyli (engl. programming style) on joukko ohjeita tai tapoja, joita ohjelmoija noudattaa koodia kirjoittaessaan. Näihin tapoihin lasketaan muun muassa sisennyksen syvyys, muuttujien ja funktioiden nimeämiskäytännöt, välilyöntien käyttö lauseissa sekä monet muut tyyliseikat. Ohjelmointityylejä on useita erilaisia, ja tällä kurssilla opetetaan noudattamaan tiettyjä tyyliin liittyviä sääntöjä.
Ohjelmointivirhe eli bugi (engl. bug) on virhe ohjelman lähdekoodissa. Bugien seurauksena ohjelma ei välttämättä käynnisty ollenkaan, kaatuu, voi joissain tilanteissa toimia väärin ja joskus aiheuttaa jopa erittäin vakavia tietoturvaongelmia. Huolellinen ohjelmointi ja testaaminen – myös harvinaisilla nurkkatapauksilla – vähentää bugien todennäköisyyttä. Ohjelman havaitun virheellisen toiminnan aiheuttavan koodin etsimistä ja korjaamista kutsutaan debuggaukseksi.
Oletusarvo (engl. default value) on arvo, joka annetaan funktion valinnaisella parametrille mikäli sitä vastaavaa argumenttia ei annettu funktiota kutsuttaessa. Esimerkiksi def kysy_pituus(kysymys, maksimi=10): -määrittelyrivillä maksimi on valinnainen parametri, jonka oletusarvo on 10.
Ominaisuus (attribute) liittyy objekteihin siten, että objekteilla voidaan sanoa olevan ominaisuuksia. Tällä kurssilla useimmat näistä ominaisuuksista ovat metodeja, mutta ne voivat olla myös arvoja. Objektin ominaisuutta käsitellään notaatiolla, jossa objektin nimen ja ominaisuuden nimen väliin tulee piste, esim: valinta.lower()-metodikutsussa valinta on objekti ja lower on ominaisuus.
  1. Kuvaus
  2. Esimerkit
Operaatio-nimitystä (engl. operation) käytetään esimerkiksi matemaattisille operaatioille. Yleisesti ottaen puhutaan operaatiosta, kun koodirivillä esiintyy operaattori ja operandeja. Esimerkiksi 5 + 5 on operaatio.
Operaattori (engl. operator) on matematiikassa ja ohjelmoinnissa nimitys symboleille, jotka kuvaavat jotain operaatiota. Operaattorilla on aina vähintään yksi operandi, mutta useimmilla kaksi. Esimerkiksi +-merkki on yhteenlaskuoperaattori.
Operandi (engl. operand) on hieno matematiikassa ja ohjelmoinnissa käytössä oleva nimitys arvoille joita käytetään operaatiossa. Esimerkiksi 5 + 8 on yhteenlaskuoperaatio, jonka operandit ovat 5 ja 8. Operandien voidaan siis sanoa olevan operaatioiden kohteita.
Paikallinen muuttuja (eng. local variable) on muuttuja, joka on määritelty vain yhdessä kontekstissa, tyypillisesti - ja erityisesti tällä kurssilla - funktion sisällä (ml. funktion parametrit). Paikalliseen muuttujaan ei voi koskea ulkopuolelta, minkä lisäksi se lakkaa olemasta kun ohjelman suoritus poistuu sen kontekstista - eli yleensä kun funktion suoritus päättyy.
  1. Kuvaus
  2. Lisämuotoilu
Paikanpidin (engl. placeholder) on yleisesti tilapäinen merkintä, joka on tarkoitus korvata toisella. Tällä kurssilla sitä käytetään lähinnä merkkijonojen muotoilun yhteydessä. Paikanpidin merkkijonon sisällä merkitään aaltosulkeilla (f"Hei {nimi}"). Merkkijonojen paikanpitimissä voi olla lisämäärityksiä kuten näytettävien desimaalien lukumäärä (f"Aaseilla on keskimäärin {keskiarvo:.2f} jalkaa"). Paikanpitimien tilalle sijoitetaan sisällä olevan muuttujan tai lausekkeen arvo.
Pakeneminen (engl. escape) tarkoittaa ohjelmoinnissa sitä, että jokin merkki tulkitaan eri tavalla kuin normaalisti. Esimerkiksi "n" on vain n-kirjain, mutta "\n" on rivinvaihto – tässä siis \-merkki (kenoviiva, engl. backslash) aiheuttaa sen, että n-merkin normaali merkitys paetaan ja korvataan toisella merkityksellä; kenoviiva toimii siis koodinvaihtomerkkinä (engl. escape character). Yksi tyypillinen käyttö on sisällyttää "-merkki merkkijonoon, joka on rajattu "-merkeillä: "aasin korvien väli on 14\""
  1. Kuvaus
  2. Esimerkit
Palauttaminen (engl. return) tapahtuu aina kun funktion suoritus päättyy. Tyypillisesti funktion palauttama(t) arvo(t) määritellään funktion sisällä return-lauseella. Funktiota kutsuvassa koodissa paluuarvo näkyy funktiokutsun paikalla, jolloin se voidaan esimerkiksi tallentaa muuttujaan tai laittaa eteenpäin toiselle funktiolle.
Paluuarvo (engl. return value) on nimitys arvolle tai arvoille, jotka funktio palauttaa, kun sen suoritus päättyy - eli siis funktion tulos. Pythonissa funktiolla voi olla useita paluuarvoja. Koodia lukiessa paluuarvoa voi käsitellä päässään siten, että funktiokutsun paikalle sijoitetaan funktion paluuarvo sen jälkeen kun funktio on suoritettu. Paluuarvo löytyy funktion sisältä return-lauseen yhteydestä. return True -rivillä on yksi paluuarvo – totuusarvo True.
Parametri (engl. parameter) on funktion määrittelyssä nimetty muuttuja. Parametreihin sijoitetaan funktion saamien argumenttien arvot silloin, kun funktiota kutsutaan. Parametri on siis nimitys jota käytetään, kun puhutaan arvojen siirtymisestä funktion näkökulmasta. def kysy_syote(kysymys, virheviesti): -rivillä siis kysymys ja virheviesti ovat parametreja. Parametrille voidaan määrittää oletusarvo jonka se saa, jos sitä vastaavaa argumenttia ei anneta kutsuttaeassa – tämä tekee kyseisestä argumentista valinnaisen.
Parametrisaatio tarkoittaa jonkin prosessin käyttötarkoitusten laajentamista siten, että osa sen kiinteistä arvoista muutetaan muuttujiksi jolloin sama prosessi voidaan suorittaa eri arvoilla ja saada eri tulos. Esim. matemaattiset yhtälöt ovat eräänlaista parametrisaatiota: kaikki yhtälön kuvaavat pisteet tuotetaan samalla kaavalla vaihtamalla muuttujan (esim. x) arvoa. Ohjelmoinnissa parametrisaatio on hyvin konkreettista: tyypillisesti prosessi irrotetaan omaan funktioonsa jonka parametrit määrittävät mitkä arvot eivät ole kiinnitettyjä jolloin funktio toimii eri tavalla eri parametreilla, antaen esim. eri tuloksen.
  1. Kuvaus
  2. Esimerkit
Poikkeus (engl. exception) on ohjelmointikielessä määritelty virhetilanne. Poikkeuksella on tyyppi (esimerkiksi TypeError), jota voi käyttää poikkeuksen käsittelyssä ohjelman sisällä sekä myös apuna virhetilanteen ratkaisussa. Tyypillisesti poikkeukseen liitetään myös viesti, joka kertoo mistä ongelmassa on kyse. Pythonissa poikkeuksia käsitellään try-except-rakenteilla.
  1. Kuvaus
  2. try-except-else-finally
Poikkeusten käsittely (engl. exception handling) on ohjelmointikieleen sisäänrakennettu keino ohjelmoijalle reagoida poikkeuksiin. Pythonissa poikkeusten käsittely onnistuu try-except-rakenteella, jossa sekä try: että except: aloittavat omat lohkonsa; try-lohkon alle kirjoitetaan se koodi, joka mahdollisesti aiheuttaa jonkun tietyn poikkeuksen ja except-lohkon alle taas se koodi, joka suoritetaan siinä tapauksessa, että kyseinen poikkeus tapahtuu. Joissain muissa ohjelmointikielissä except-avainsanan sijaan käytetään avainsanaa catch, minkä takia yleisesti puhutaan poikkeusten kiinni ottamisesta.
  1. Kuvaus
  2. Esimerkit
Polku (engl. path) on tiedoston tai kansion sijainti kiintolevyllä. Polku voi olla absoluuttinen tai relatiivinen. Absoluuttinen polku sisältää kaikki kansiot aina juureen asti (esim. Windowsissa asemakirjain kuten C:), kun taas relatiivinen sisältää kansiot aktiiviseen kansioon asti (ts. siihen kansioon mistä ohjelma käynnistettiin). Polku esitetään ohjelmointikielissä yleensä merkkijonona, ja polun osat erotetaan kauttaviivalla /. Useimmiten polkuja muodostaessa kannattaa käyttää os.path-moduulin join-funktiota.
  1. Kuvaus
  2. Esimerkit
Interaktiivinen Python-tulkki (engl. interactive Python interpreter) tai Python-konsoli (engl. Python console) on ohjelma, johon voi kirjoittaa Python-koodirivejä. Nimitys ”interaktiivinen” tulee siitä, että koodirivi suoritetaan välittömästi sen syöttämisen jälkeen, ja ohjelma näyttää käyttäjälle koodirivin tuottaman paluuarvon (esimerkiksi matemaattisen operaation tuloksen). Kurssilla suositellaan IPython-tulkkia, joka käynnistetään asennuksen jälkeen komennolla ipython.
Python-tulkki (engl. Python interpreter) on ohjelma, joka muuttaa Python-koodin tietokoneelle annettaviksi ohjeiksi. Se vastaa niin kooditiedostojen kuin myös interaktiiviseen Python-tulkkiin kirjoitettujen komentojen suorittamisesta. Tällä kurssilla sanalla tulkki viitataan kuitenkin useimmiten nimenomaan interaktiiviseen Python-tulkkiin.
Pythonissa pääohjelma (engl. main program) on se osa koodia, joka suoritetaan, kun ohjelma käynnistetään. Nyrkkisääntönä pääohjelma sisältää kaikki lauseet sekä ohjausrakenteet jotka ovat kiinni koodin vasemmassa laidassa. Pääohjelma sijaitsee tyypillisesti koodin lopussa, ja useimmiten if __name__ == "__main__":-lauseen alla. Älä kuitenkaan käytä tätä lausetta alkupään harjoitustehtävissä, koska tarkistin ei pysty tällöin suorittamaan koodisi pääohjelmaa.
  1. Kuvaus
  2. Esimerkit
Rajapinta (engl. interface) viittaa yleisesti kahden eri asian välimaastoon, ja ohjelmoinnissa sillä tarkoitetaan erityisesti tapaa, jolla ohjelman eri osat voivat liittyä toisiinsa. Esimerkiksi funktion rajapinnasta puhuttaessa tarkoitetaan sitä muotoa, jossa funktio vastaanottaa tietoa ja suoriutumisen jälkeen antaa käsiteltyä tietoa tai jonkun lopputuloksen ulos. Kirjastoilla on yleensä olemassa jonkinlainen niin kutsuttu API, eli Application Programming Interface, joka kertoo sen, kuinka kirjaston toiminnallisuuksia käytetään. Ihmiset taas ovat ohjelmiin kytköksissä käyttöliittymän (engl. User Interface, lyh. UI) kautta, joka sekin on tietynlainen rajapinta.
Ratkaisumalli on ohjelmoijan muodostama abstrakti ajatus siitä miten ohjelmointiongelman ratkaisu etenee. Ratkaisumalli ei ole vielä koodia, mutta sen tulisi olla yksiselitteinen sekä selkeisiin välivaiheisiin jakaantuva, jotta sen pohjalta voidaan kirjoittaa ohjelma. Ratkaisumallia voi hahmotella päänsisäisesti, käyttämällä avuksi paperia sekä kokeilemalla asioita Python-tulkissa.
  1. Kuvaus
  2. Esimerkit
Rekursio (engl. recursion) on yleinen ohjelmointitermi, joka viittaa siihen, kun funktio kutsuu itseään. Rekursio on siis funktiopohjainen tapa luoda toistorakenne, jossa funktio välittää itselleen uusia argumentteja ja käsittelee omia paluuarvojaan. Rekursio on kätevä esimerkiksi puumaisia rakenteita käsitellessä – käsitellään yksi ”oksa” jollain tavalla, ja sitten rekursion avulla käsitellään tästä oksasta lähtevät oksat ja niin edelleen. Pythonissa rekursiota käytetään aika vähän. Osasyynä on sisäänrakennettu rekursiorajoitus, joka asettaa katon sille, kuinka monta kertaa funktio saa kutsua itseään.
Relatiivinen polku (relative path) on käyttöjärjestelmäkäsite, joka kertoo hakemiston tai tiedoston osoitteen suhteessa aktiiviseen hakemistoon (eli siihen missä komento suoritetaan). Relatiivinen polku ei välitä siitä millainen hakemistoviidakko on levyaseman juuren ja aktiivisen hakemiston välissä. Tästä johtuen relatiivista polkua käytetään yleensä ohjelman omiin alikansioihin viittaamiseen. Tällöin ohjelma alikansioineen voidaan siirtää toiseen paikkaan ilman, että polkuja tarvii muuttaa.
Rivinvaihtomerkki (engl. newline, line break, end of line; lyh. EOL) eli "\n" on merkki, joka tulostettaessa tai tiedostoon kirjoitettaessa saa aikaan rivinvaihdon. Jos merkkijonoa tarkastellaan ilman printtausta esim. konsolissa, rivinvaihdot näkyvät "\n"-merkkeinä.
  1. Kuvaus
  2. Määrittely
  3. Arvojen haku
  4. Sanakirjan muuttaminen
Sanakirja (engl. dictionary) on tietorakenne, jossa arvoille annetaan avaimet (yleensä merkkijono). Sanakirjan merkittävin etu on se, että selkeästi nimetyt avaimet tekevät tietorakennetta käsittelevästä koodista huomattavasti selkeämpää luettavaa. Python 3.7:sta lähtien sanakirjan avaimet ja arvot ovat siinä järjestyksessä missä ne on lisätty.
Sapluuna (engl. template) on muotti esimerkiksi tekstille, joka käyttäjälle halutaan näyttää, mutta joka ei semmoisenaan ole vielä valmis. Sapluunasta siis puuttuu tietoa, joka on tarkoitus saada sapluunan paikanpitimien tilalle.
Kurssilla yleisin sapluuna on merkkijono, jossa on paikanpitimiä format-metodia varten.
Sekvenssi (engl. sequence) on mikä tahansa arvo Pythonissa, jossa on tavaraa peräkkäin – esimerkiksi merkkijono, lista ja monikko kuuluvat näihin.
Matematiikasta tuttu sidontajärjestys (engl. precedence) määrittää sen, missä järjestyksessä lausekkeen operaatiot suoritetaan.
lopputulos = 10 + 2 * (2 + 3)
Yllä olevan koodin lopputulos on 20, sillä ensin lasketaan yhteen luvut 2 ja 3, joiden summa kerrotaan kahdella, ja johon lopuksi lasketaan vielä yhteen luku 10. Esimerkissä korkein presedenssi on siis sulkeilla, toisiksi korkein kertolaskulla ja matalin yhteenlaskulla.
Sijoittaminen (engl. assignment) liittyy muuttujiin ja arvoihin. Tyypillinen ilmaisu on ”muuttujaan sijoittaminen”, joka yksinkertaistettuna tarkoittaa sitä, että tietty arvo annetaan muuttujalle (x = 5). Tarkennettuna muuttujaan sijoittaminen kuitenkin tarkoittaa Pythonissa sitä, että muuttujan ja arvon välille luodaan viittaus – muuttuja tietää mistä arvo löytyy.
Samaa tarkoittavia ilmaisuja ovat mm. muuttujaan tallentaminen, arvon ottaminen ylös muuttujaan, arvoon viittaminen muuttujalla, arvon tunkeminen muuttujaan... jne.
Sijoitusoperaattoria (engl. assignment operator) eli =-merkkiä käytetään muuttujaan sijoituksessa. Operaattoria käytettäessä kohteena olevan muuttujan tulee aina olla sen vasemmalla puolen ja sijoitettavan arvon (tai lausekkeen, joka tuottaa sijoitettavan arvon) sen oikealla puolen.
Silmukkamuuttuja (engl. loop variable) on for-silmukan määrittelrivillä esitelty muuttuja, joka saa yksitellen kaikki läpikäytävän sekvenssin (esim. lista) arvot. Sen arvo siis vaihtuu jokaisella silmukan kierroksella. Yksinkertainen esimerkki materiaalista: for elain in elukoita:, jossa siis elain on silmukkamuuttuja. Mikäli läpikäytävä sekvenssi sisältää monikoita (tai listoja), silmukkamuuttujia voi olla myös useita: for opiskelija, arvosana in arvostelu:. Silmukkamuuttujat eivät ole erillisessä nimiavaruudessa, joten niiden tulee erota muista funktion/pääohjelman muuttujista.
Sisennetyn (engl. indented) koodirivin edessä on tyhjää eli välilyöntejä tai sarkainmerkkejä. Sisennyksen tehtävä on parantaa koodin luettavuutta yleisesti. Pythonissa sisennys myös erottaa koodilohkot toisistaan - kaikki samalla sisennystasolla olevat rivit ovat samaa lohkoa. Tällä kurssilla käytetään välilyöntejä, ja yksi sisennys on 4 välilyöntiä. Kaikki järkevät tekstieditorit saa syöttämään sarkainmerkin sijaan halutun määrän välejä.
Sisäänrakennetut funktiot (engl. builtin functions) ovat funktioita, jotka tulevat Pythonin mukana, ja niitä käyttääkseen ei tarvitse erikseen ottaa käyttöön mitään moduulia/kirjastoa.
Suorittaminen (engl. execution) tai ajaminen (engl. running) tarkoittaa ohjelman tai koodinpätkän koneistettua läpi käymistä, jolloin ohjelmassa tai koodissa määritellyt asiat tapahtuvat. Python-tulkki suorittaa sille annettua koodia lause kerrallaan – tällöin ohjelman sanotaan olevan käynnissä. Kun enempää suoritettavaa koodia ei ole, törmätään käsittelemättömään virheeseen tai koodissa erikseen niin määrätään, ohjelman suorittaminen päättyy.
Suoritusjärjestys (eng. presedence) määrittää missä järjestyksessä koodirivillä olevat lauseet suoritetaan. Eri tyyppisillä operaatioilla on eri prioriteetti suoritusjärjestyksesä. Ne löytyvät alla olevasta linkistä. Samantasoiset operaatiot suoritetaan vasemmalta oikealle. Kuten matematiikassa, suoritusjärjestykseen voi vaikuttaa suluilla.
Syntaksi (engl. syntax) on koodin kielioppi. Esimerkiksi Pythonin syntaksi määrittää, millainen teksti on tulkittavissa Python-koodiksi. Jos teksti ei noudata koodin syntaksia, sitä ei voida suorittaa. Syntaksi antaa myös koodaajalle tietoa siitä, missä muodossa halutunlainen ohje tulee antaa.
Syntaksivirhe (engl. syntax error) on poikkeus, joka syntyy, kun Python-tulkki tutkii kooditiedostoa ennen sen suorittamista ja havaitsee siellä rikkinäistä – eli jollain tapaa väärin kirjoitettua – koodia. Syntaksivirhe estää koodin suorittamisen.
Yksi hyvin yleinen syntaksivirhe on sulkujen auki jääminen. Tällöin syntaksivirheilmoitus näyttää poikkeuksen tapahtuneen vasta virheellistä riviä seuraavalla rivillä! Muista siis mystisen syntaksivirheviestin äärellä katsoa myös edeltäviä rivejä.
  1. Kuvaus
  2. Esimerkit
Syöte (engl. input) on tämän kurssin puitteissa käyttäjältä pyydetty tekstimuotoinen komento tai vastaus kysymykseen. Syöte kysytään input-funktiolla, ja se on aina merkkijono. Aina, kun ohjelma kysyy syötettä, sen suoritus pysähtyy, kunnes käyttäjä on antanut syötteen.
Takaisinkutsu (engl. callback) on yleinen ohjelmoinnissa käytetty menetelmä, jossa funktio ottaa parametrin kautta vastaan funktion kutsuttavakseen heti (synkroniset takaisinkutsut) tai joskus tulevaisuudessa (asynkroniset takaisinkutsut). Nimensä menetelmä on saanut soittopyynnöstä: kutsuttavaa funktiota, jolle jokin funktio välitetään argumenttina, ”pyydetään” kutsumaan tätä annettua funktiota. Pythonissa listojen sort()-metodin key-parametri on esimerkki callback-funktioiden käytöstä. Usein käyttöliittymiä toteutettaessa käyttöliittymäelementteihin kytketään callback-funktioita.
Tallennusformaatti on tiedoston "syntaksi", joka siis kertoo miten data on tiedostoon tallennettu. Tallennusformaatti asettaa rajat sille millaista dataa tiedostossa voidaan esittää. Sen perusajatus on se, että koodissa olevat tietorakenteet voidaan tallentaa tiedostoon jossain muodossa, ja myöhemmin ladata sieltä uudelleen. Tallennusformaatti voi seurata jotain alan standardia (esim. JSON), mutta lopullisesti on ohjelman tekijän vastuulla päättää mitkä tiedot ovat ohjelman kannalta relevantteja ja miten ne on paras esittää.
Tapahtuma (engl. event) on ohjelmointikäsite, jota käytetään yleisesti interaktiivisten sovellusten, jotka pyörivät reaaliajassa, yhteydessä. Näissä sovelluksissa on yleensä pääsilmukka, joka tarkkailee tapahtumia, joita voivat olla esimerkiksi: käyttäjä klikkaa hiirellä, käyttäjä painaa näppäimistön nappia, tietty aika on kulunut jne. Tapahtumiin voidaan kiinnittää käsittelijäfunktioita, jolloin funktiota kutsutaan aina kun tapahtuma havaitaan. Tällä tavalla helpotetaan merkittävästi interaktiivisten sovellusten ohjelmointia, koska itse sovellusta kirjoittaessa ei tarvitse huolehtia siitä miten ruudun tapahtumat tunnistetaan.
Periaatteessa tekstitiedosto (engl. text file) on mikä tahansa tiedosto, jonka sisältö voidaan lukea nätisti tekstieditorilla. Tämän kurssin kontekstissa kuitenkin tekstitiedosto on erityisesti sellainen tiedosto, jonka sisältöä käsitellään tekstinä Pythonissa. Eli siinä missä kooditiedostoja suoritetaan, tekstitiedostoja käytetään datan varastoimiseen ohjelman ajojen välillä.
Terminaali (engl. terminal), komentokehote (engl. prompt) ja komentorivi (engl. command line) ovat eri nimiä käyttöjärjestelmän tekstipohjaiselle käyttöikkunalle. Komentorivillä annetaan tekstikomentoja käyttöjärjestelmälle. Tällä kurssilla pääasiassa siirrytään cd- eli change directory -komennolla hakemistosta toiseen ja käytetään ipython-komentoa kooditiedostojen suorittamiseen sekä interaktiivisen tulkin avaamiseen.
  • Windowsissa komentoriville pääsee kirjoittamalla käynnistä-valikon hakuun cmd
  • Mac OS X -käyttöjärjestelmässä komentorivin saa auki kirjoittamalla Finderiin pääte (suomen kielisissä versioissa) tai terminal (englannin kielisissä versioissa)
  • Linux-työpöytäympäristöistä voit painaa Ctrl + Alt + T työpöydällä tai kirjoittaa hakuun {{terminal}}}
Testaamalla eli kokeilemalla (engl. test) selvitetään, toimivatko hartaasti näppäillyt koodirivit halutulla tavalla. Testejä suorittamalla siis etsitään koodista mahdollisia ohjelmointivirheitä. Ohjelmien testaaminen on jopa niin olennaista, että joidenkin alan työntekijöiden tehtävänä on ainoastaan automatisoitujen testien ohjelmointi. Lovelace-järjestelmän tarkistimet testaavat järjestelmään lähetetyt koodit.
Tiedostokahva (engl. file handle) on erityinen objekti, jota Pythonissa käytetään avattuun tiedostoon viittaamiseen. Huomattavaa on, että kahva ei ole sama asia kuin tiedoston sisältö, mutta sen kautta voidaan lukea tiedoston sisältö tai kirjoittaa tiedostoon. Tiedostokahva saadaan käyttöön open-funktiolla, jolle määritetään avattavan tiedoston sijainti sekä avausmoodi, jossa se avataan, esim: with open("aasi.txt", "r") as tiedosto: avaa aasi.txt-tiedoston with-lauseessa (jossa tiedostot tulee yleensä avata) siten, että muuttujasta nimeltä tiedosto löytyy tiedostokahva.
Tiedostonimi (engl. filename) on tiedoston koko nimi, joka sisältää varsinaisen tiedostolle annetun nimen sekä tiedostopäätteen. Esimerkiksi aasisvengaa.py on kokonainen tiedoston nimi, jossa varsinainen annettu nimi on aasisvengaa ja pääte on .py.
Koodin sisällä tiedostojen nimet esitetään merkkijonoina.
Tiedostopääte (engl. filename extension) on se osa tiedoston nimestä, joka on viimeisen pisteen oikealle puolen. Tiedostopäätteet ovat yleisesti käytetty tapa tiedostojen sisällön tunnistamiseen, eli ne ovat keino ilmaista tiedoston tyyppiä. Esimerkiksi kuvatiedostoilla voi olla vaikkapa .png- tai .jpg-pääte, kun taas Python-kooditiedoston pääte on yleensä .py, kuten nimessä aasisvengaa.py.
Tietorakenne (engl. data structure) on yleisnimitys kokoelmille, jotka sisältävät useita arvoja. Tietorakenteen tarkoitus on siis säilöä useammasta kuin yhdestä arvosta koostuvaa tietoa jollain lukuisista eri tavoista, joille kullekin yleensä on olemassa tietorakenteen helpon hyödyntämisen mahdollistavat ohjelmointikeinot, kuten arvojen lisääminen, poistaminen ja muokkaaminen tietorakenteesta. Tietorakenne on siis ohjelman sisäinen tapa käsitellä dataa siten, että varsinaiset yksityiskohdat on piilotettu ohjelmoijalta, joka käyttää tietorakenteita koodissaan.
Omissa ohjelmissa käytettävät tietorakenteet tulisikin valita siten, että niitä on helppo käsitellä koodissa, ja että ne palvelevat hyvin ohjelman tarkoitusta. Pythonissa yleisimmät tietorakenteet ovat lista, monikko ja sanakirja. Myös set – eli joukko-opillinen joukko, joka ei sisällä duplikaattiarvoja – on käytännöllinen tietorakenne. Sisäänrakennettujen lisäksi lukuisia käytänöllisiä tietorakenteita löytyy collections-moduulista.
Myöhemmillä kursseilla tutuksi tulevat myös muun muassa tärkeät tietorakenteet puu (engl. tree) ja graafi (engl. graph).
Tila (engl. state) viittaa sananmukaisesti ohjelman tilanteeseen. Käytännössä ohjelman tila kattaa kaikki sen tila-avaruuteen (engl. state space) kuuluvat asiat, kuten muuttujien arvot, tiedostoissa olevan datan ja sen, mitä kohtaa koodista sillä hetkellä ollaan suorittamassa. Taattu keino saada aikaiseksi korjauskelvotonta spagettikoodia on käyttää niin kutsuttua globaalia tilaa (engl. global state) – rikos, johon syyllistyvät epäpuhtaat globaaleja muuttujia hyödyntävät funktiot.
Myöhemmillä, ohjelmoinnin käsitteitä formaalimmin tutkivilla kursseilla tutuksi tulevat muun muassa tilakoneet (engl. state machine) sekä tilattomat (engl. stateless) että tilalliset (engl. stateful) ohjelmat.
Toistorakenne tai silmukka (engl. loop) on ohjausrakenne, jonka alaisuuteen kirjoitettua koodia toistetaan joko tietty lukumäärä toistoja tai kunnes jokin ehto lakkaa toteutumasta. Toistorakenteiden avulla ohjelmat pystyvät jouhevasti palaamaan aiempaan kohtaan suoritusta ja niillä voidaan myös helposti käsitellä suuria määriä arvoja. Toistorakenteita ovat Pythonissa for- ja while-silmukat.
Tosi (engl. true) on toinen kahdesta mahdollisesta totuusarvosta ja toisen, eli epätoden, vastakohta. Sitä voidaan pitää lopputuloksena loogisissa ja vertailuoperaatorioissa, jotka pitävät paikkansa. Esimerkiksi vertailuoperaatio 5 > 4 pitää paikkansa, joten kyseinen operaatio evaluoituu todeksi. Pythonissa totta merkitään avainsanalla True.
Totuusarvo (engl. boolean) on yleensä ohjelmointikielien yksinkertaisin tietotyyppi, jolla on vain kaksi arvoa: tosi (Pythonissa True) ja epätosi (Pythonissa False). Vertailuoperaattorit tuottavat totuusarvoja, ja niitä käytetään yleisimmin ehtolauseiden ja while-silmukoiden yhteydessä. Pythonissa kaikki arvot vastaavat jompaa kumpaa totuusarvoa. Yleisesti ns. tyhjät arvot (0, "", None jne.) ovat sama kuin False, ja loput sama kuin True.
  1. Kuvaus
  2. Esimerkit
Traceback – taaksepäin jäljittäminen – viittaa yleiseen tapaan tutkia virhetilanteen syntymistä sen juurilta alkaen. Python-tulkki tulostaa virhetilanteessa virheviestin, johon olennaisesti kuuluu traceback. Traceback esitetään purkamalla ohjelman funktiokutsuista syntynyt pino, jonka viimeisimmässä osassa varsinainen virhe tapahtui. Tästä syystä tracebackeja kutsutaan myös nimellä stacktrace, eli pinon jäljittäminen. Jos esimerkiksi pääohjelmassa kutsutaan funktiota f, josta käsin kutsutaan funktiota g, jossa virhe tapahtuu, funktiokutsujen pino on muotoa pääohjelmafg.
  1. Kuvaus
  2. Esimerkit
Tulostaminen (engl. print) onkin ohjelmoinnissa jotain muuta – joskaan ei lopulta periaatteiltaan kovin erilaista – kuin paperin ja musteen yhdistämistä halutunlaisiksi sivuiksi. Tietokoneohjelmien yhteydessä tulostamisella tarkoitetaan tekstin tuottamista esiin näytölle, erityisesti terminaaliin. Pythonissa tätä varten on oma funktio, print(...), joka tulostaa sille annetun argumentin terminaaliin.
  1. Kuvaus
  2. Esimerkit
Tynkäfunktio (engl. stub function) on funktio, jolle on kirjoitettu oikeanlainen määrittelyrivi parametreineen, mutta ei oikeaa sisältöä. Niitä yleensä kirjoitetaan koodiin ohjelman rakennetta suunniteltaessa sekä mahdollistamaan funktioiden kutsuminen muualle ohjelmassa siten, että sitä voidaan testata ennen kuin funktioiden toteutus on valmis. Tynkäfunktion sisältönä on usein pelkkä pass, joku informatiivinen tulostus tai jonkin oletusarvon palautus. Isommissa Python-ohjelmissa tynkäfunktioissa on tapana aiheuttaa NotImplementedError-poikkeus, jolloin reitti toteuttamattoman funktion kutsuun on helppo löytää tracebackistä.
Tyylisäännöt ovat kokoelma suosituksia, joiden mukaan koodia tulisi kirjoittaa. Kullakin kielellä on yleensä omansa. Tyylisääntöjen rikkominen ei varsinaisesti riko ohjelmaa, mutta tyylisääntöjen mukainen koodi on miellyttävämpää lukea ja usein tästä johtuen myös helpompi korjata. Tällä kurssilla seurataan Pythonin virallista tyylistandardia erityisesti tekstikenttätehtävissä. Myös tiedostotehtävissä on koodin laadun tarkistus, jossa käytetään PyLint-ohjelmaa.
  1. Kuvaus
  2. Esimerkit
Tyyppi (engl. type) on arvon ominaisuus – jokainen arvo edustaa aina jotain tiettyä tyyppiä. Tyypin tarkoitus on siis kertoa, minkälaisesta arvosta on kyse. Käytännössä tästä seuraa myös se, mitä operaatioita arvoilla voi tehdä, ja mitä metodeja niiltä löytyy. Funktiot on myös miltei aina toteutettu siten, että niille syötettävien argumenttien täytyy olla tietyntyyppisiä, jotta funktio voisi toimia. Tyypit ovat yksi ohjelmoinnin keskeisimmistä käsitteistä.
Pythonissa arvojen sopiminen koodista löytyviin operaatioihin tarkistetaan tilannekohtaisesti näiden arvon ominaisuuksien perusteella – ei siis suoraan itse tyyppiä tarkastamalla. Esimerkiksi useimmissa tapauksissa kokonaisluku ja liukuluku kelpaavat molemmat, mutta on myös tapauksia, joissa näin ei ole (esimerkiksi merkkijonoa ei voi kertoa liukuluvulla).
Tällä kurssilla tyypillisiä tyyppejä ovat kokonaisluku (int), liukuluku (float), merkkijono (str), lista (list), totuusarvo (bool) ja monikko (tuple). Myös funktioilla on oma tyyppinsä!
  1. Kuvaus
  2. Esimerkit
Tyyppimuunnos (engl. type casting, type conversion, type coercion) tarkoittaa sananmukaisesti jonkin koodissa esiintyvän muuttujan tai literaaliarvon tyypin muuntamista toiseksi. Pythonissa tähän törmää usein, kun käyttäjältä on saatu merkkijonona luku, jota halutaan käsitellä esimerkiksi kokonais- tai liukulukuna. Käytännössä tämä onnistuu esimerkiksi lauseilla int("123") tai float("3.14"). Joissain tilanteissa Python-tulkki suorittaa tyyppimuunnoksen automaattisesti, kuten laskettaessa yhteen kokonais- ja liukulukuja.
  1. Kuvaus
  2. Esimerkit
Vakio (engl. constant) on nimetty literaaliarvo. Niitä käytetään erityisesti silloin kun sama literaaliarvo esiintyy koodissa useasti. Yleisesti ottaen nimetty vakio on käytännöllisempi kuin koodissa oleva literaaliarvo, koska sen nimestä voi päätellä mitä se tekee. Samaten jos arvoa tarvitsee muuttaa, vakioita käyttäessä tarvitsee muuttaa vain kohtaa jossa se määritellään. Pythonissa ei ole erillistä tapaa luoda vakioita vaan ne ovat periaatteessa ihan vain muuttujia. Vakioiden nimet kirjoitetaan isolla. Esimerkiksi VASTAUS = 42.
Funktiota kutsuttaessa argumentti on valinnainen (engl. optional argument), jos funktiossa sitä vastaavalle parametrille on määritetty oletusarvo. Tällöin kyseistä argumenttia ei ole pakollista antaa funktiokutsussa. Jos valinnaisia argumentteja on useita, ne annetaan tyypillisesti avainsana-argumentteina.
Vertailuarvoa käytetään esim. listojen järjestämisessä. Vertailuarvo on listan alkiosta johdettu arvo, jota käytetään järjestämisperusteena. Esimerkiksi jos lista sisältää listoja, vertailuarvo voi olla jostain tietystä indeksistä otettu alkio. Se voi olla myös monimutkaisempi johdannainen, kuten listan alkioiden summa tai keskiarvo.
Vertailuoperaattoreita (engl. comparison operators) käytetään, kun verrataan arvoja toisiinsa. Ne ovat matematiikasta tuttuja ja niillä voidaan verrata suuruksia. Vertailuoperaattorit palauttavat totuusarvon True tai False riippuen vertailun lopputuloksesta. Vertailuoperaattoreita ovat <, <=, >, >=, == ja !=.
Ohjelmoinnin asiayhteydessä viittaaminen (engl. reference) tarkoittaa tapaa, jolla muuttuja liittyy arvoonsa. Viittauksen kohde on tietokoneen muisti, ja muuttuja itsessään – konepellin alla – sisältää osoitteen, mistä kohdasta tietokoneen muistia siihen liitetty arvo löytyy.
  1. Kuvaus
  2. Esimerkit
Virheviesti (engl. error message) on Python-tulkin tapa ilmoittaa koodissa tapahtuneesta poikkeuksesta. Virheviestiin kuuluu tieto siitä missä kohdassa koodin suoritusta se tapahtui, mikä rivi kooditiedostossa aiheutti poikkeuksen, poikkeuksen tyyppi (esimerkiksi SyntaxError) sekä lyhyt sanallinen kuvaus. Virheviestit ovat ohjelmoijan paras ystävä, ja niiden lukeminen on erittäin oleellinen ohjelmointitaito. Niitä ei siis ole syytä säikähtää, sillä ne auttavat selvittämään, mikä ohjelman koodissa on pielessä!
  1. Kuvaus
  2. Esimerkit
Avainsana break on erityinen komento, jota käytetään toistorakenteissa. Se päättää silmukan suorituksen välittömästi, ja koodin suoritus jatkuu ensimmäiseltä silmukan jälkeiseltä riviltä. Jos silmukassa oli else-osa, siihen ei mennä.
  1. Kuvaus
  2. Esimerkit
continue on toinen toistorakenteisiin liittyvä avainsana (toisen ollessa break). Toisin kuin break, joka lopettaa koko silmukan suorituksen, continue keskeyttää ainoastaan meneillään olevan kierroksen suorituksen – suoritus jatkuu siis seuraavasta kierroksesta. Huomaa, että tätä avainsanaa tarvitaan vain tilanteissa, joissa halutaan jättää osa kierroksesta suorittamatta, eli silmukan kierroksen loppuun ei ole tarpeen laittaa continue-avainsanaa.
  1. Kuvaus
  2. Esimerkit
enumerate on Pythonissa sisäänrakennettu funktion kaltainen erityisobjekti, joka tuottaa generaattoriobjektin. Sitä käytetään pääasiassa for-silmukoissa silloin, kun on tarpeen saada läpi käytävän listan alkioiden indeksit käyttöön silmukan sisällä. enumerate-objekti tuottaa monikkoja, joissa ensimmäisenä on alkion indeksi ja toisena itse alkio. Käyttöesimerkki: for i, hahmo in enumerate(muumilaakso):.
f(ormat)-merkkijono on erityinen merkkijono, jota käytetään, kun halutaan tuottaa tekstiä jonka sisään on sijoitettu muuttujien tai yksinkertaisten lausekkeiden arvoja. Tällainen merkkijono merkitään laittamalla sen eteen pieni f-kirjain. Merkkijonon sisällä voidaan käyttää aaltosulkeilla merkittyjä paikanpitimiä, joiden sisälle voidaan kirjoittaa muuttujien nimiä tai lausekkeita.
Esimerkki: f"Aasin korvien väli on {mittaus:.2f} tuumaa" (olettaen että mittaus-niminen muuttuja on aikaisemmin määritelty).
  1. Kuvaus
  2. Esimerkit
Pythonissa for on toinen silmukkatyyppi. Sen käyttötarkoitus on tietorakenteiden läpikäyminen – iterointi. Sitä käytetään erityisesti listojen kanssa. Yleisesti ottaen for-silmukkaa käytetään silloin, kun pystytään ennalta määrittämään montako kierrosta silmukkaa tulee pyörittää. Tietorakenteiden läpikäynnin lisäksi näihin lukeutuu tietyn toistomäärän tekeminen (esimerkiksi kymmenen toistoa). Silmukan määrittelyrivi on muotoa: for alkio in lista:, jossa alkion paikalle tulee silmukkamuuttujan nimi ja listan paikalla ilmoitetaan läpikäytävä tietorakenne.
  1. Kuvaus
  2. Esimerkit
Pythonissa moduuleja otetaan käyttöön import-lauseella. Normaalikäytössä (esim. import math) lause tuo ohjelmaan uuden nimiavaruuden, joka on sama kuin moduulin nimi. Tämän nimen kautta päästään käsiksi moduulin funktioihin. Nimistä voi myös tuoda ainoastaan osan from-import-lauseella: from math import ceil. Moduulille voidaan myös antaa eri nimi as-avainsanalla: import math as m.
  1. Kuvaus
  2. Esimerkit
Silmukoista while pohjautuu toistoon ehdon tarkastelun kautta - silmukan sisällä olevaa koodilohkoa suoritetaan niin kauan kuin silmukalle annettu ehto on tosi. Ehto määritetään samalla tavalla kuin ehtolauseissa, esim: while summa < 21. While-silmukat soveltuvat parhaiten sellaisiin tilanteisiin, joissa ei voida etukäteen selvittää montako toistoa tarvitaan - erityisesti syötteiden kysyminen käyttäjältä on tällainen tilanne.
  1. Kuvaus
  2. Esimerkit
Pythonissa with on hieman muista poikkeava avainsana, sillä se ei ole varsinaisesti ohjausrakenne tai uuden asian määrittely. Tällä kurssilla sitä käytetään pääasiassa tiedostojen avaamisessa, tyyliin with open("aasi.txt") as tiedosto:. Tiedoston luku- tai kirjoitusoperaatiot suoritetaan with-lauseen alla. Kun with-lohko päättyy, Python huolehtii automaattisesti with-lauseessa avattujen tiedostojen yms. sulkemisesta.