2.26. Podizanje pogrešaka¶
Funkcija može signalizirati problem svom pozivatelju podizanjem iznimke. Ključna riječ je raise:
def square_root(x):
if x < 0:
raise ValueError("square_root expects a non-negative number")
return x ** 0.5
Poziv square_root(-1) zaustavlja se na retku raise, izlazi iz funkcije i traži odgovarajući except u pozivatelju. Ako je nijedan pozivatelj ne uhvati, skripta završava s tragom poziva (traceback).
2.26.1. Zašto podizati umjesto vraćanja stražarske vrijednosti¶
Dva načina za prijavu „loš ulaz”:
# 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
Oblik s iznimkom obično je bolji:
Pozivatelj mora namjerno obraditi slučaj pogreške – bilo pomoću
try, bilo dopuštanjem da se iznimka proširi. Stražarske vrijednosti lako se zaborave i lako se zamijene s normalnim rezultatom.Poruka o pogrešci putuje uz iznimku; pristup sa stražarskom vrijednošću mora pričvrstiti dijagnostiku negdje drugdje.
Zadano ponašanje pri neobrađenoj iznimci je glasno rušenje s tragom poziva koji upućuje na sporni poziv. Tiho vraćanje
Nonepostaje suptilna pogreška kasnije.
Posegnite za stražarskim vrijednostima samo kada je „nije pronađeno” rutinski, neiznimni ishod – dict.get() vraća None na nedostajući ključ upravo zato što se očekuje da pretraživanja ponekad promaše.
2.26.2. Prilagođene klase iznimki¶
Da biste podigli problem koji bi pozivatelj možda želio razlikovati od ugrađenih pogrešaka, definirajte podklasu Exception:
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)
Prazno tijelo class je u redu – samo ime je ono što je bitno, jer pozivatelji hvataju prema klasi. Grupirajte povezane pogreške pod zajedničkom osnovom ako bi pozivatelj možda želio uhvatiti cijelu obitelj u jednom bloku.
2.26.2.1. Ponovno podizanje¶
Goli raise unutar except bloka ponovno podiže trenutnu iznimku tako da se širi do sljedećeg rukovatelja:
try:
do_work()
except Exception as e:
log(e)
raise # let it keep going
Ovo je pravi oblik kada funkcija želi promatrati pogrešku (zabilježiti je, prebrojati je, poništiti djelomičnu promjenu) bez stvarne obrade.
2.26.3. Kada hvatati, a kada propustiti dalje¶
Korisno pravilo:
Uhvatite iznimku na razini koja se može smisleno oporaviti – zamijenite zadanom vrijednošću, ponovite pokušaj, preskočite loš ulaz.
Pustite je da se proširi kada nema ničeg korisnog za učiniti osim rušenja, ili kada je sloj iznad onaj koji zna kako se oporaviti.
Funkcija u sredini stoga poziva koja proguta svaku pogrešku i tiho se vrati čini neuspjehe nemogućima za praćenje. Preferirajte puštanje iznimki da putuju dok ne dođu do koda koji zaista ima plan.