Ko pride do napake Python sestavi objekt razreda
Exception
(po slovensko, izjema), oziroma, točneje, enega
od razredov, izpeljanih iz Exception
. Vsak razred
predstavlja svojo vrsto napake. Teh je
ogromno, pa še nove si lahko izmišljamo. Ko sestavi ta objekt, ga
vrže (throw
) oz. sproži (raise
). V Pythonu se
uporablja slednji izraz, raise
, v Cju podobnih jezikih pa
prvi, throw
. Če vržene izjeme nihče ne ulovi, se ta izpiše,
kot smo vajeni. Tule se bomo naučili loviti, potem pa še metati
izjeme.
Če predvidevamo, da lahko v določenem delu kode pride do napake
(kakor bomo tule po domače govorili izjemam), ga zapremo v blok
try-except
(in ne try-catch
, ko bi se reklo v
C-ju in žlahti). Takole je videti.
= input("Višina: ")
visina = input("Teža: ")
teza try:
= float(visina)
visina = float(teza)
teza except ValueError:
print(f"Napačen vnos")
Višina: velik
Teža: težak
Napačen vnos
Besedi try
sledi dvopičje in, kot smo vajeni, zamaknjen
blok, ki seveda lahko vsebuje tudi več kot eno vrstico. Nato pride
except
, kjer, če smo vljudni, povemo, kakšne vrste napako
pričakujemo. Znotraj bloka except
storimo kaj pametnega -
izpišemo kakšno obvestilo ali kaj podobnega.
Blokov except
je lahko tudi več, če pričakujemo več
različnih napak.
Če nismo vljudni, izpustimo vrsto napake in pišemo samo
except:
. Tak except
ujame vse napake.
Ko pride do napake, se izvajanje bloka prekine. Python poišče prvi
blok except
, ki se ujema z vrsto napake in začne izvajati
kodo v njem.
Tule bi se morda zdela boljša ideja:
try:
= input("Višina: ")
visina = input("Teža: ")
teza = float(visina)
visina = float(teza)
teza = teza / visina ** 2
bmi print(f"Indeks telesne teže: {bmi:.2f}")
except ValueError:
print(f"Napačen vnos")
Višina: 1.85
Teža: 76
Indeks telesne teže: 22.21
Vendar ni, saj bi bila potem dobra ideja tudi to:
try:
with open("stevilke.txt") as f:
= 0
vrstic = 0
v for vrstica in f:
+= int(vrstica)
v += 1
vrstic = v / vrstic
povp print(f"Povprečje: {povp:.2f}")
except IOError:
print("Datoteka ne obstaja")
except ValueError:
print("Napačna vrednost v datoteki")
except ZeroDivisionError:
print("Datoteka je prazna")
except:
print("Neznana napaka")
Povprečje: 6.33
Pa ni. To je zelo slaba ideja.
except
ne sme loviti preveč na široko.
except ValueError
smo napisali, da prestrežemo morebitno
napako v int(vrstica)
, torej naj lovi le napako v tej
vrstici. Ideja try-except
ni loviti napak na počez. Če neke
napake nismo predvideli, je boljše, da je ne prestrežemo. Program ne sme
teči naprej, kot da je vse v redu, če ni. Še manj sme, recimo, izpisati,
da datoteka vsebuje napačno vrednost, če problem morda sploh ni v
datoteki, temveč je napaka ValueError
priletela od kod
drugod.
Loviti z except:
pa je sploh skoraj prepovedano. To
storite samo, ko kličete neko (tujo) kodo, v kateri se lahko zgodi
karkoli.
Pravilneje bi bilo torej tako:
try:
with open("stevilke.txt") as f:
= 0
vrstic = 0
v for vrstica in f:
try:
+= int(vrstica)
v except ValueError:
print("Napačna vrednost v datoteki")
continue
+= 1
vrstic try:
= v / vrstic
povp except ZeroDivisionError:
print("Datoteka je prazna")
= 0 # da se ima v naslednji vrtici kaj izpisati ...
povp print(f"Povprečje: {povp:.2f}")
except IOError:
print("Datoteka ne obstaja")
Povprečje: 6.33
except IOError
je neroden, vendar neizogiben, če želimo
datoteko odpreti z with
. In ja, zoprn je tudi zato, ker
lahko (vsaj načelno) pride do IOError
tudi ob branju
datoteke.
Edina napaka, ki jo malo težje predvidimo, je ValueError
v int(vrstica)
. Vse ostale lahko predvidimo in
preprečimo.
import os
if not os.path.exists("stevilke.txt"):
print("Datoteka ne obstaja")
else:
with open("stevilke.txt") as f:
= 0
vrstic = 0
v for vrstica in f:
try:
+= int(vrstica)
v except ValueError:
print("Napačna vrednost v datoteki")
continue
+= 1
vrstic if vrstic == 0:
print("Datoteka je prazna")
else:
= v / vrstic
povp print(f"Povprečje: {povp:.2f}")
Povprečje: 6.33
Vrnimo se k indeksu telesne teže. Rekli smo, da je tole slaba ideja.
try:
= input("Višina: ")
visina = input("Teža: ")
teza = float(visina)
visina = float(teza)
teza = teza / visina ** 2
bmi print(f"Indeks telesne teže: {bmi:.2f}")
except ValueError:
print(f"Napačen vnos")
Višina: 1.85
Teža: 76
Indeks telesne teže: 22.21
Kako to popraviti? Imamo namreč problem.
= input("Višina: ")
visina = input("Teža: ")
teza try:
= float(visina)
visina = float(teza)
teza except ValueError:
print(f"Napačen vnos")
= teza / visina ** 2
bmi print(f"Indeks telesne teže: {bmi:.2f}")
Višina: 1.85
Teža: 76
Indeks telesne teže: 22.21
Problem je v tem: če se zgodi napaka, potem sta visina
in teza
se vedno niza. Strašno nerodno bi bilo pisati:
= input("Višina: ")
visina = input("Teža: ")
teza try:
= float(visina)
visina = float(teza)
teza except ValueError:
print(f"Napačen vnos")
= teza = None
visina
if visina is not None:
= teza / visina ** 2
bmi print(f"Indeks telesne teže: {bmi:.2f}")
Višina: 1.85
Teža: 76
Indeks telesne teže: 22.21
except
-u sme slediti else
. Kar napišemo
znotraj else
, se izvede le, če ni prišlo do izjeme.
= input("Višina: ")
visina = input("Teža: ")
teza try:
= float(visina)
visina = float(teza)
teza except ValueError:
print(f"Napačen vnos")
else:
= teza / visina ** 2
bmi print(f"Indeks telesne teže: {bmi:.2f}")
Končno lahko napišemo tudi finally
. Ta lahko sledi
exceptu
oz. else
-u, lahko pa imamo celo samo
try
in finally
.
Kar se nahaja znotraj bloka finally
se izvede v vsakem
primeru - najsi je prišlo do izjeme ali ne.
try:
1 / 0
finally:
print("Zgodilo se je nekaj izjemnega.")
Zgodilo se je nekaj izjemnega.
---------------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
Cell In[25], line 2
1 try:
----> 2 1 / 0
3 finally:
4 print("Zgodilo se je nekaj izjemnega.")
ZeroDivisionError: division by zero
Napaka se izpiše, predtem pa se izvede tudi blok
finally
.
Objekt, ki predstavlja izjemo, lahko vsebuje različne podatke,
povezane z njo. Če nas zanimajo,
except <vrsta-izjeme>
nadaljujemo z
as <ime>
.
try:
open("te-datoteke-ni.txt")
except IOError as napaka:
print(napaka)
print(napaka.filename)
print(napaka.strerror)
[Errno 2] No such file or directory: 'te-datoteke-ni.txt'
te-datoteke-ni.txt
No such file or directory
IOError
je zelo zgovoren, drugi manj. Vsaj vrsto napake
in sporočilo pa lahko vedno izvemo.
Včasih želimo napako sprožiti sami. To je preprosto. Zapomniti si
moramo le, da v Pythonu tega ne storimo s throw
, kot v
jezikih s Cjevsko sintakso, temveč z raise
.
raise ValueError("Čudna vrednost.")
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[37], line 1
----> 1 raise ValueError("Čudna vrednost.")
ValueError: Čudna vrednost.