Skip to content

Try

Pythonissa on tyypillistä "tehdä ensin ja pyytää tarpeen mukaan anteeksi". Tähän käytetään try-except-finally -lauseketta. Varsinkin ulkopuolisen rajapinnan, kuten jonkin palvelun REST API:n, käyttö on aina virheherkkää.

  • Verkko voi olla alhaalla
  • Palvelin voi olla nurin
  • Sertifikaatti on voinut mennä vanhaksi
  • Token on voinut mennä vanhaksi
  • Palvelin saattaa olla kiireinen ja palauttaa HTTP status 503:n
  • ...tai jotain muuta!

Sokkona luottamisen sijasta on turvallisempaa kääriä API-kutsu try-lausekkeen sisään ja käsitellä mahdolliset virheet except-lausekkeessa. Tämän jälkeen koodi jatkaa suoritusta normaalisti. Muutoin mahdollinen virhetilanne, josta syntyy jokin Exception, kaataa sinun Python-sovelluksen suorituksen.

Peruskäyttö

Try-lauseketta käyttäessä vähintään yksi except-lauseke on pakollinen. Finally on valinnainen eikä sitä tarvitse läheskään aina. Finally-lausekkeen sisällä voi esimerkiksi sulkea tietokantayhteyden tai tiedoston, joka on avattu try-lausekkeen sisällä, tai suorittaa jotakin muuta puhdistusta.

try:
    # Do something
    pass
except:
    # Handle exception
    pass
finally:
    # Do something after try-except
    pass

Alla yksinkertainen esimerkki, joka kaatuu tässä tapauksessa ZeroDivisionErroriin. Mikäli koodi kaatuisi mihin tahansa muuhun virheeseen, seuraava except-blokki ottaa sen vastaan, tulostaa viestin ruudulle, ja lopulta nostaa virheen. Virheiden tarkempi käsittely ja lokiin kirjoittaminen on seuraavien Python-kurssien aihe.

try: 
    x = 10 / 0
except ZeroDivisionError:
    print("You can't divide by zero!")
except Exception as e:
    print("Something else went wrong! Error: " + str(e))
    raise

Pidempi esimerkki

Jotta virheen synnyn ymmärtäisi, tulee kurkata sen kirjaston dokumentaatiota (tai lähdekoodia), jota olet käyttämässä. Pythonissa on täysin sallittua luoda omia virheitä, joten et voi lähtökohtaisesti tietää, mitä virheitä ajamasi kirjasto voi nostaa. Alla esimerkkikoodia, joka edustaa ovea, joka on ehkä lukossa, ehkä ei. Mikäli oven yrittää tiirikoida auki, voi tiirikka mennä rikki.

Käyttäjä saa itse asettaa oven lukossa olemisen mahdollisuuden sekä tiirikan rikkoutumisen mahdollisuuden.

import random 

class DoorLockedException(Exception):
    pass

class LockpickBrokeException(Exception):
    pass

class Door:
    def __init__(self, locked_chance: float, lockpick_break_chance: float):
        self.locked = random.random() < locked_chance
        self.lockpick_break_chance = lockpick_break_chance
        self.closed = True

    def open(self):
        if self.locked:
            raise DoorLockedException()
        else:
            self.closed = False

    def lockpick(self):
        if random.random() < self.lockpick_break_chance:
            raise LockpickBrokeException()
        else:
            self.locked = False

    def is_closed(self) -> bool:
        return self.closed

Alla koodi, joka pyrkii avaamaan oven ensin ihan vain nykäisemällä. Tämän jälkeen oven tiedetään olevan lukossa, joten jatkossa pitää yrittää tiirikkaa.

# Kokeile muuttaa locked_chace ja lockpick_break_chance arvoja
door = Door(locked_chance=0.95, lockpick_break_chance=0.80)

# Yritetään kerran ilman tiirikkaa, loput tiirikalla
retries = 6
we_know_it_is_locked = False


for retry in range(retries):
    try:
        if we_know_it_is_locked:
            print("Kokeillaan tiirikkaa")
            door.lockpick()
        door.open()
    except DoorLockedException:
        print("Ovi on lukossa. Kaivetaan tiirikat pussista.")
        we_know_it_is_locked = True
    except LockpickBrokeException:
        print("... murtovälineet meni rikki, yritetään uudestaan")
    except:
        print("Jotain meni pieleen, softassa on vikaa vikaa vikaa...")
        raise

    if not door.is_closed():
        print(f"[SUCCESS] Ovi on auki. Vaati {retry} tiirikkaa.")
        break
else:
    print(f"[FAILURE] Ovi ei auennut {retry} tiirikalla.")