CHEM-A2600 - Kemiantekniikan ohjelmointikurssi, Luento-opetus, 24.4.2023-30.6.2023
This course space end date is set to 30.06.2023 Search Courses: CHEM-A2600
Python-oppimateriaali (CHEM-A2600)
rivi.strip()
Virhetilanteiden käsittely (try-except-finally)
Poikkeukset
Hyvässä ohjelmakoodissa varaudutaan aina erilaisiin virhetilanteisiin, kuten
- Käyttäjän antama virheellinen syöte
- Tiedoston avaaminen epäonnistuu
Pythonissa virhetilanteet hoidetaan poikkeusten (engl. exception) avulla.
Oletkin varmasti jo kohdannut lukuisia poikkeuksia kurssin aikana. Esimerkiksi jos olet yrittänyt muuntaa vääränlaista merkkijonoa lukuarvoksi komennolla
luku = int("kolme")
Python on ilmoittanut ValueError-poikkeuksesta:
ValueError: invalid literal for int() with base 10: 'kolme'
Poikkeusten "nappaaminen" ohjelmakoodissa
Virhetilanteessa Python "nostaa" (engl. raise) poikkeuksen. Poikkeuksen voi "napata" (engl. catch) ja käsitellä, jolloin se ei johda ohjelman suorituksen keskeytymiseen.
Poikkeusten nappaamiseen ja käsittelemiseen käytetään try-except -rakennetta. Napataan virheellisestä lukuarvosta johtuva ValueError-poikkeus:
while True: try: luku = float(input("Anna liukuluku:\n")) # Jos suoritus jatkui tänne, käyttäjä antoi kelvollisen liukuluvun break except ValueError: # Napataan ValueError-poikkeus (virheellinen lukuarvo) print("Virheellinen lukuarvo!") # Virhe on nyt käsitelty ja ohjelman suoritus palaa while-silmukan alkuun print("Annoit luvun", luku)
Esimerkkisuoritus:
Anna liukuluku: > kolme piste kaksi Virheellinen lukuarvo! Anna liukuluku: > 3.2 Annoit luvun 3.2
Poikkeus tiedostoa avattaessa (OSError)
Tiedostoja käsitellessä voi tulla vastaan virhetilanteita (esimerkiksi yritetään avata tiedostoa, jota ei ole olemassa). Tällöin pitää napata virhe OSError:
# Luetaan rivit tiedostosta try: tiedosto = open("rivit.txt", "r") for rivi in tiedosto: print(rivi) except OSError: print("Virhe avattaessa tiedostoa rivit.txt")
Sisäkkäiset try-lausekkeet
Monesti tarvitaan sisäkkäisiä try-lausekkeita, joilla hoidetaan erityyppiset virheet. Hyvä nyrkkisääntö on, että try-avainsana tulisi olla mahdollisimman lähellä riviä, jossa odotat virheen tapahtuvan. Esimerkki:
nimi = "luku.txt" try: # Yritetään avata tiedosto, tämä voi johtaa virheeseen tiedosto = open(nimi, "r") # Tiedosto aukesi onnistuneesti, luetaan siitä for rivi in tiedosto: try: # Yritetään muuntaa teksti liukuluvuksi luku = float(rivi) # Onnistui, tulostetaan luku print("Tiedosto sisälsi luvun", luku) except ValueError: # float()-funktio aiheutti ValueError-virheen print(f"Virheellinen lukuarvo {rivi.strip():s} tiedostossa {nimi:s}") # Suljetaan lopuksi tiedosto tiedosto.close() except OSError: # Tänne päädytään, jos open-funktio epäonnistui print("Virhe avattaessa tiedostoa", nimi)
Jos kaikki menee hyvin, ohjelma tulostaa:
Tiedosto sisälsi luvun 1.0
Jos tiedostoa ei ole olemassa, ohjelma tulostaa:
Virhe luettaessa tiedostoa luku.txt
Jos tiedostossa on virheellisiä lukuja, ohjelma tulostaa esimerkiksi
Virheellinen lukuarvo 1.0a tiedostossa luku.txt
Tiedosto sisälsi luvun 2.0
try-except-finally
try-except-finally-rakenteella voidaan varmistaa, että jokin asia tehdään varmasti, vaikka virheitä syntyisi. Luetaan tekstiä tiedostosta ja napataan poikkeukset (tässä tapauksessa tiedosto sisältää ainoastaan tekstin "keukeu"):
nimi = "teksti.txt" try: # Yritetään avata tiedosto, tämä voi johtaa virheeseen tiedosto = open(nimi, "r") try: # Tiedosto aukesi onnistuneesti, yritetään lukea tiedoston sisältö read()-funktiolla teksti = tiedosto.read() # Onnistui, tulostetaan sisältö print("Tiedosto sisälsi tekstin", teksti) except OSError: # read()-funktio aiheutti OSError-virheen, tiedostoa ei voi lukea print(f"Tiedostoa {nimi:s} ei voitu lukea") finally: # Suljetaan tiedosto riippumatta siitä, oliko virheitä vai ei print("Suljetaan tiedosto") tiedosto.close() except OSError: # Tänne päädytään, jos open-funktio epäonnistui. print("Virhe avattaessa tiedostoa", nimi) # Tiedostoa ei avattu, joten sitä ei tarvitse myöskään sulkea
Jos kaikki menee hyvin, ohjelma tulostaa:
Tiedosto sisälsi tekstin keukeu Suljetaan tiedosto
Jos tiedostoa ei ole olemassa, ohjelma tulostaa:
Virhe avattaessa tiedostoa teksti.txt
Jos tiedostossa on virheellisiä lukuja, ohjelma tulostaa
Tiedostoa teksti.txt ei voitu lukea Suljetaan tiedosto
Ohjelma siis sulkee viimeisenä tekonaan tiedoston. Tämä on tärkeää ja tiedostojen kanssa tuleekin aina käyttää try-finally -rakennetta. Helpoiten tämän vaatimuksen voi kuitata käyttämällä with-lausetta (ks. seuraava luku).
try-except-else(-finally)
try-except-rakenteeseen voi yhdistää myös else-osan, joka suoritetaan siinä tapauksessa, että try-osio ei aiheuttanut poikkeuksia:
try: luku = float(input("Anna luku:\n")) except ValueError: print("Virheellinen luku") else: print("Annoit luvun", luku)
try-except-else-rakenteeseen voi yhdistää vielä finally-osan, jossa esimerkiksi suljetaan avatut tiedostot.
Lista Pythonin poikkeuksista
Mistä tietää, mikä poikkeus pitäisi napata? Tässä on listä Pythonin sisäänrakennetuista poikkeuksista. Tällä kurssilla tärkeimmät poikkeukset ovat OSError (virhe avattaessa tai käsitellessä tiedostoa) ja ValueError (virhe muunnettaessa tekstiä lukuarvoksi).
Monimutkaisemmissa ohjelmissa pitää ottaa huomioon myös erilaisten kirjastojen poikkeustilanteet. Oikeassa ohjelmistossa, jonka tehtävä on esimerkiksi valvoa kriittistä tuotantoprosessia, voikin olla enemmän virheenkäsittelykoodia kuin varsinaista toiminnallista koodia!
Tehtävä 5.5.1