7.1 Tworzenie niestandardowych wyjątków
Czasami standardowe wyjątki w Pythonie nie spełniają w pełni potrzeb twojej aplikacji. W takich sytuacjach możesz stworzyć własne wyjątki, dziedzicząc je z klasy bazowej Exception
lub jakiejkolwiek innej odpowiedniej klasy wyjątków.
Podstawy tworzenia niestandardowych wyjątków
Tworzenie niestandardowego wyjątku polega na zdefiniowaniu nowej klasy, która dziedziczy po klasie bazowej Exception
. Możesz dodać własne metody i atrybuty do swojej klasy wyjątków, aby dostarczyć dodatkowe informacje o błędzie.
Przykład tworzenia prostego niestandardowego wyjątku
Krok 1: Definiowanie niestandardowego wyjątku
class MyCustomError(Exception):
"""Klasa dla niestandardowego wyjątku."""
pass
Krok 2: Użycie niestandardowego wyjątku
def check_value(value):
if value < 0:
raise MyCustomError("Wartość nie powinna być mniejsza od zera")
try:
check_value(-1)
except MyCustomError as e:
print(f"Zdarzył się niestandardowy wyjątek: {e}")
To bardzo proste. Najważniejsze, aby twój wyjątek dziedziczył po klasie Exception
lub jednym z jej potomków.
7.2 Tworzenie wyjątku z dodatkowymi atrybutami
Możesz dodawać atrybuty i metody do swojej klasy wyjątków, aby przekazać dodatkowe informacje o powstałym błędzie.
Przykład:
class NegativeValueError( Exception ):
"""Klasa dla niestandardowego wyjątku przy ujemnej wartości."""
def __init__(self, value, message = "Wartość nie powinna być mniejsza od zera"):
self.value = value
self.message = message
super().__init__(self.message)
def __str__(self):
return f'{self.message}: {self.value}'
Użycie wyjątku z dodatkowymi atrybutami
def check_value(value):
if value < 0:
raise NegativeValueError(value)
try:
check_value(-1)
except NegativeValueError as e:
print(f"Zdarzył się niestandardowy wyjątek: {e}")
Nasz wyjątek to klasa, która dziedziczy po klasie Exception
. Dlatego możesz z nim zrobić wszystko to, co z każdą inną klasą: dodawać pola, metody, parametry konstruktora itp.
Wszystko dla twojej wygody i wygody programistów, którzy będą przechwytywać twoje wyjątki.
7.3 Tworzenie hierarchii niestandardowych wyjątków
Dla bardziej zaawansowanych aplikacji warto tworzyć hierarchie niestandardowych wyjątków. Pozwala to grupować powiązane wyjątki i ułatwia ich obsługę.
Przykład:
class ApplicationError(Exception):
"""Klasa bazowa dla wszystkich wyjątków aplikacji."""
pass
class NegativeValueError(ApplicationError):
"""Klasa dla niestandardowego wyjątku przy ujemnej wartości."""
def __init__(self, value, message="Wartość nie powinna być mniejsza od zera"):
self.value = value
self.message = message
super().__init__(self.message)
def __str__(self):
return f'{self.message}: {self.value}'
class ValueTooLargeError(ApplicationError):
"""Klasa dla niestandardowego wyjątku przy zbyt dużej wartości."""
def __init__(self, value, message="Wartość jest za duża"):
self.value = value
self.message = message
super().__init__(self.message)
def __str__(self):
return f'{self.message}: {self.value}'
Użycie hierarchii niestandardowych wyjątków
def check_value(value):
if value < 0:
raise NegativeValueError(value)
elif value > 100:
raise ValueTooLargeError(value)
try:
check_value(150)
except NegativeValueError as e:
print(f"Zdarzył się wyjątek: {e}")
except ValueTooLargeError as e:
print(f"Zdarzył się wyjątek: {e}")
except ApplicationError as e:
print(f"Ogólny wyjątek aplikacji: {e}")
7.4 Kolejność przechwytywania wyjątków
Podczas przechwytywania wyjątków, zwłaszcza z jednej hierarchii, ważne jest, aby ustawić je w odpowiedniej kolejności. I chociaż kod w blokach except
nigdy nie jest wykonywany jednocześnie, chodzi o to, że klasa bazowa wyjątku jest w stanie przechwycić wyjątki wszystkich klas potomnych.
Na przykład kod:
def check_value(value):
if value < 0:
raise NegativeValueError(value)
elif value > 100:
raise ValueTooLargeError(value)
try:
check_value(150)
except ApplicationError as e: # przechwyci wyjątki typu ApplicationError i wszystkich jego potomków
print(f"Ogólny wyjątek aplikacji: {e}")
W bloku except
zostaną przechwycone wyjątki typu ApplicationError
i wszystkich jego klas potomnych.
A ponieważ wszystkie wyjątki są potomkami klasy Exception
, taki kod przechwyci w zasadzie wszystkie wyjątki:
def check_value(value):
if value < 0:
raise NegativeValueError(value)
elif value > 100:
raise ValueTooLargeError(value)
try:
check_value(150)
except Exception as e:
print(f"Przechwycenie wszystkich wyjątków: {e}")
except ApplicationError as e: # Ten kod nigdy się nie wykona
print(f"Ogólny wyjątek aplikacji: {e}")
Przechwytywanie wyjątków w bloku except ApplicationError
nigdy nie nastąpi, ponieważ blok except Exception
przechwyci w zasadzie wszystkie wyjątki typu Exception
i jego potomków.
Rozwiązanie
Dlatego przechwytywanie wyjątków odbywa się w kolejności odwrotnej do dziedziczenia: im bliżej klasa do klasy Exception
, tym niżej.
Przykład:
def check_value(value):
if value < 0:
raise NegativeValueError(value)
elif value > 100:
raise ValueTooLargeError(value)
try:
check_value(150)
except NegativeValueError as e:
print(f"Zdarzył się wyjątek: {e}")
except ApplicationError as e:
print(f"Ogólny wyjątek aplikacji: {e}")
except Exception as e:
print(f"Przechwycenie wszystkich wyjątków: {e}")
W tym przykładzie bloki except
są ułożone w kolejności, która odpowiada ich hierarchii dziedziczenia: najpierw przechwytywane są bardziej specyficzne wyjątki, takie jak NegativeValueError
, a następnie bardziej ogólne, takie jak ApplicationError
. To pozwala poprawnie obsłużyć wyjątki i unikać sytuacji, w której bardziej ogólny handler przechwyci wyjątek zanim zrobią to bardziej specjalistyczne bloki except
.
GO TO FULL VERSION