2.26. Virheiden nostaminen¶
Funktio voi ilmoittaa ongelmasta kutsujalleen nostamalla poikkeuksen. Avainsana on raise:
def square_root(x):
if x < 0:
raise ValueError("square_root expects a non-negative number")
return x ** 0.5
square_root(-1) -kutsu pysähtyy raise -rivillä, hyppää ulos funktiosta ja etsii sopivaa except -lausetta kutsujasta. Jos yksikään kutsuja ei nappaa sitä, skripti päättyy jäljitysvuohon (traceback).
2.26.1. Miksi nostaa poikkeus vartioarvon palauttamisen sijaan¶
Kaksi tapaa raportoida ”virheellinen syöte”:
# signal with a sentinel
def square_root_or_none(x):
if x < 0:
return None
return x ** 0.5
# raise an exception
def square_root(x):
if x < 0:
raise ValueError("...")
return x ** 0.5
Poikkeusmuoto on yleensä parempi:
Kutsujan on tarkoituksellisesti käsiteltävä virhetapaus – joko
try-lauseella tai antamalla poikkeuksen edetä. Vartioarvot on helppo unohtaa ja helppo erehtyä pitämään normaalina tuloksena.Virheviesti kulkee poikkeuksen mukana; vartioarvomenetelmän on liitettävä diagnostiikka jonnekin muualle.
Oletuskäyttäytyminen käsittelemättömälle poikkeukselle on äänekäs kaatuminen jäljitysvuohella, joka osoittaa virheellistä kutsua. Hiljaiset
None-palautukset muuttuvat hienovaraisiksi bugeiksi myöhemmin.
Tartu vartioarvoihin vain, kun ”ei löytynyt” on rutiininomainen, poikkeukseton tulos – dict.get() palauttaa None puuttuvalla avaimella juuri siksi, että hakujen oletetaan toisinaan epäonnistuvan.
2.26.2. Mukautetut poikkeusluokat¶
Nostaaksesi ongelman, jonka kutsuja saattaa haluta erottaa sisäänrakennetuista virheistä, määritä Exception -luokan aliluokka:
class ConfigError(Exception):
pass
def load_config(path):
try:
f = open(path)
except OSError as e:
raise ConfigError("missing config file: " + path)
try:
load_config("settings.json")
except ConfigError as e:
print("startup failed:", e)
Tyhjä class -runko on hyvä – nimi itse on se, mikä merkitsee, koska kutsujat nappaavat luokan mukaan. Ryhmittele toisiinsa liittyvät virheet yhteisen perusluokan alle, jos kutsuja saattaa haluta napata koko perheen yhdessä lohkossa.
2.26.2.1. Uudelleennostaminen¶
Paljas raise except -lohkon sisällä nostaa nykyisen poikkeuksen uudelleen, jolloin se etenee seuraavalle käsittelijälle:
try:
do_work()
except Exception as e:
log(e)
raise # let it keep going
Tämä on oikea muoto, kun funktio haluaa havainnoida virheen (kirjata sen lokiin, laskea sen, perua osittaisen muutoksen) varsinaisesti käsittelemättä sitä.
2.26.3. Milloin napata ja milloin antaa edetä¶
Hyödyllinen nyrkkisääntö:
Nappaa poikkeus tasolla, joka voi mielekkäästi toipua – korvata oletuksella, yrittää uudelleen, ohittaa virheellisen syötteen.
Anna sen edetä, kun ei ole mitään hyödyllistä tehtävissä kaatumisen lisäksi, tai kun yläpuolinen kerros on se, joka tietää miten toipua.
Kutsupinon keskellä oleva funktio, joka nielee jokaisen virheen ja palauttaa hiljaa, tekee vioista jäljittämättömiä. Suosi sitä, että poikkeukset kulkevat, kunnes ne saavuttavat koodin, jolla aidosti on suunnitelma.