2.26. Vyvolávání chyb¶
Funkce může svému volajícímu signalizovat problém vyvoláním výjimky. Klíčovým slovem je raise:
def square_root(x):
if x < 0:
raise ValueError("square_root expects a non-negative number")
return x ** 0.5
Volání square_root(-1) se zastaví na řádku raise, vyskočí z funkce a hledá odpovídající except ve volajícím. Pokud ji žádný volající nezachytí, skript skončí s výpisem zásobníku.
2.26.1. Proč vyvolat výjimku místo vrácení sentinelu¶
Dva způsoby, jak ohlásit „špatný vstup“:
# 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
Forma s výjimkou je obvykle lepší:
Volající musí chybový případ záměrně ošetřit – buď pomocí
try, nebo tím, že nechá výjimku se šířit. Sentinely je snadné zapomenout a snadné je zaměnit za normální výsledek.Chybová zpráva cestuje s výjimkou; přístup se sentinelem musí diagnostiku připojit někam jinam.
Výchozím chováním u nezpracované výjimky je hlasitý pád s výpisem zásobníku, který ukazuje na problematické volání. Tiché návraty
Nonese později stanou skrytými chybami.
Po sentinelech sahejte jen tehdy, když je „nenalezeno“ běžným, neviýjimečným výsledkem – dict.get() vrací None u chybějícího klíče právě proto, že se očekává, že vyhledávání někdy minou.
2.26.2. Vlastní třídy výjimek¶
Chcete-li vyvolat problém, který by mohl chtít volající odlišit od vestavěných chyb, definujte podtřídu 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)
Prázdné tělo class je v pořádku – důležitý je samotný název, protože volající zachytávají podle třídy. Seskupte související chyby pod společný základ, pokud by volající mohl chtít zachytit celou rodinu v jednom bloku.
2.26.2.1. Opětovné vyvolání¶
Holé raise uvnitř bloku except znovu vyvolá aktuální výjimku, takže se šíří k další obslužné rutině:
try:
do_work()
except Exception as e:
log(e)
raise # let it keep going
Toto je správný tvar, když chce funkce chybu pozorovat (zaznamenat ji, započítat, vrátit zpět částečnou změnu), aniž by ji skutečně zpracovala.
2.26.3. Kdy zachytit a kdy nechat šířit¶
Užitečné pravidlo:
Zachyťte výjimku na úrovni, která se z ní může smysluplně zotavit – nahradit výchozí hodnotou, zopakovat, přeskočit špatný vstup.
Nechte ji šířit, když není nic užitečného k udělání kromě pádu, nebo když vrstva nad ní je ta, která ví, jak se zotavit.
Funkce uprostřed zásobníku volání, která spolkne každou chybu a tiše se vrátí, činí selhání nevysledovatelnými. Dejte přednost tomu nechat výjimky cestovat, dokud nedosáhnou kódu, který má skutečně nějaký plán.