Contents

Jak tworzyć niestandardowe wyjątki w Pythonie

Wbudowane klasy wyjątków Pythona nie obsługują pewnych sytuacji błędów, które mogą pojawić się w kodzie. W takich przypadkach należy utworzyć niestandardowe wyjątki, aby skutecznie obsłużyć te błędy.

W dziedzinie języków programowania, takich jak Python, możliwe jest tworzenie dostosowanych klas wyjątków, które mogą być zgłaszane podczas wystąpienia określonych scenariuszy błędów. Wykorzystując te niestandardowe typy wyjątków, można skutecznie zająć się bardziej precyzyjnymi i szczegółowymi kwestiami w swojej bazie kodu, ostatecznie promując zarówno przejrzystość, jak i łatwość konserwacji pod względem organizacji i czytelności kodu.

Dlaczego potrzebujesz niestandardowych wyjątków?

/pl/images/own-you-error.jpg

Podczas ewolucji oprogramowania mogą pojawić się nieprzewidziane komplikacje wynikające ze zmian w kodzie źródłowym, współpracy z dodatkowymi modułami lub bibliotekami lub współpracy ze zdalnymi aplikacjami. Skuteczne zarządzanie takimi nieprawidłowościami jest niezbędne do zapewnienia, że system może albo odzyskać siły po tych niepowodzeniach, albo wykonać procedury zakończenia w opanowany sposób.

Python oferuje szereg wbudowanych klas wyjątków, które obejmują błędy takie jak ValueError , TypeError , FileNotFoundError i inne. Chociaż te wbudowane wyjątki dobrze spełniają swoje zadanie, tylko czasami mogą dokładnie reprezentować błędy, które mogą wystąpić w aplikacji.

Dostosowanie niestandardowych wyjątków pozwala na bardziej precyzyjne dopasowanie do konkretnych potrzeb w aplikacji, zapewniając cenne informacje dla programistów, którzy mogą napotkać taki kod.

Jak definiować niestandardowe wyjątki

Aby utworzyć niestandardowe wyjątki, zdefiniuj klasę Pythona, która dziedziczy po klasie Exception . Klasa Exception oferuje podstawową funkcjonalność potrzebną do obsługi wyjątków i można ją dostosować, aby dodać funkcje oparte na konkretnych potrzebach.

Podczas opracowywania spersonalizowanych kategorii wyjątków ważne jest zachowanie prostoty przy jednoczesnym włączeniu kluczowych funkcji w celu zachowania szczegółów błędu. Menedżerowie wyjątków mogą następnie wykorzystać te cechy, aby skutecznie rozwiązywać błędy.

Oto niestandardowa klasa wyjątków, MyCustomError:

 class MyCustomError(Exception):
    def __init__(self, message=None):
        self.message = message
        super().__init__(message)

Podczas procesu inicjalizacji ta konkretna klasa pozwala na warunkowe włączenie parametru komunikatu. Wykorzystując słowo kluczowe “super”, skutecznie wywołuje konstruktor swojej poprzedniej klasy Exception, która służy jako kluczowy element protokołów obsługi wyjątków.

Jak zgłaszać niestandardowe wyjątki

Rzeczywiście, aby wywołać przeszkodę i wygenerować instancję naszej dostosowanej kategorii wyjątków, można użyć słowa kluczowego “raise” w połączeniu z przykładem niestandardowego typu wyjątku, dostarczając mu komunikat, który zawiera naturę problemu:

 if True:
    raise MyCustomError("A Custom Error Was Raised!!!.")

Można również zdecydować się na zgłoszenie wyjątku bez podawania argumentów,

 if True:
    raise MyCustomError # shorthand

Oba formaty są równie odpowiednie do tworzenia niestandardowych komunikatów o błędach.

/pl/images/raised-custom-error.jpg

Jak obsługiwać niestandardowe wyjątki

Podczas pracy z niestandardowymi wyjątkami konieczne jest zastosowanie podobnej strategii, jak w przypadku standardowych wyjątków. Obejmuje to wykorzystanie bloków try-except-finally w celu wychwycenia tych wyjątkowych okoliczności i wykonania odpowiednich środków zaradczych. W ten sposób możemy skutecznie zarządzać nieoczekiwanymi zdarzeniami, które mogą wystąpić podczas wykonywania naszego kodu.

 try:
    print("Hello, You're learning how to All Things N Custom Errors")
    raise MyCustomError("Opps, Something Went Wrong!!!.")
except MyCustomError as err:
    print(f"Error: {err}")
finally:
    print("Done Handling Custom Error")

W ten sposób jesteśmy przygotowani do radzenia sobie z każdą możliwą odmianą wyjątkowych wyjątków, które mogą się pojawić.

/pl/images/handling-custom-error.jpg

W przypadku wystąpienia wyjątku podczas wykonywania bloku try, odpowiadający mu blok except może go przechwycić i nim zarządzać. Jeśli jednak nie ma odpowiedniego bloku except do obsługi wyjątku, wówczas zostanie wykonany dowolny obecny blok finally, co doprowadzi do ponownego podniesienia wyjątku. Głównym celem użycia bloku finally powinno być przeprowadzenie operacji czyszczenia, które są obowiązkowe, niezależnie od tego, czy wyjątek ma miejsce, czy nie.

 try:
    raise KeyboardInterrupt
except MyCustomError as err:
    print(f"Error: {err}")
finally:
    print("Did not Handle the KeyboardInterrupt Error. \
Can Only Handle MyCustomError")

W tym przypadku ma miejsce przerwanie klawiatury, jednak obejmująca sekcja “except” jest ograniczona do obsługi instancji “MyCustomError”. W związku z tym, gdy wykonywana jest klauzula “finally”, poprzednio nieuznany wyjątek jest następnie przywracany.

/pl/images/re-raise-unhandled-exception.jpg

Dziedziczenie niestandardowych klas błędów

Zasada programowania obiektowego (OOP) umożliwia rozszerzenie dziedziczenia nie tylko na standardowe typy danych, ale także na spersonalizowane kategorie wyjątków, podobnie jak w przypadku typowych klas. Dzięki tej technice możliwe jest utworzenie klas wyjątków, które oferują większą przejrzystość w odniesieniu do okoliczności błędu. Metodologia ta pozwala na obsługę błędów na różnych etapach programu i skutkuje lepszym zrozumieniem przyczyny problemu.

Aby zapewnić spójną obsługę różnych scenariuszy błędów w integracji zewnętrznego API w aplikacji internetowej, zaleca się ustanowienie ustandaryzowanego podejścia poprzez utworzenie specjalnej klasy wyjątków znanej jako BaseAPIException.

 class BaseAPIException(Exception):
    """Base class for API-related exceptions."""
    def __init__(self, message):
        super().__init__(message)
        self.message = message

Aby rozszerzyć funkcjonalność niestandardowej klasy wyjątków, można utworzyć podklasy wywodzące się z klasy nadrzędnej. Proces ten pozwala na tworzenie wyspecjalizowanych wyjątków, które mają wspólne atrybuty i zachowania z klasą nadrzędną, a jednocześnie posiadają unikalne cechy dostosowane do konkretnych sytuacji.

 class APINotFoundError(BaseAPIException):
    """Raised when the requested resource is not found in the API."""
    pass

class APIAuthenticationError(BaseAPIException):
    """Raised when there's an issue with authentication to the API."""
    pass

class APIRateLimitExceeded(BaseAPIException):
    """Raised when the rate limit for API requests is exceeded."""
    pass

Podczas implementacji aplikacji internetowej ważne jest, aby obsługiwać wszelkie potencjalne błędy, które mogą pojawić się podczas wywołań API, poprzez podnoszenie i wychwytywanie określonych niestandardowych wyjątków. W ten sposób można skutecznie zarządzać tymi sytuacjami za pomocą dobrze zdefiniowanej logiki programowania dostosowanej do każdej sytuacji.

 def request_api():
    try:
        # Simulate an API error for demonstration purposes
        raise APINotFoundError("Requested resource not found.")
    except APINotFoundError as err:
        # Log or handle the 'Not Found' error case
        print(f"API Not Found Error: {err}")
    except APIAuthenticationError:
        # Take appropriate actions for authentication error
        print(f"API Authentication Error: {err}")
    except APIRateLimitExceeded:
        # Handle the rate limit exceeded scenario
        print(f"API Rate Limit Exceeded: {err}")
    except BaseAPIException:
        # Handle other unknown API exceptions
        print(f"Unknown API Exception: {err}") 

Ostateczna klauzula except jest odpowiedzialna za przechwytywanie wszelkich wyjątków, które mogą pojawić się w klasie nadrzędnej, działając jako wszechstronna obsługa błędów dla wszelkich nieprzewidzianych kwestii związanych z interfejsem programowania aplikacji (API).

/pl/images/inheriting-custom-exception.jpg

Dziedzicząc niestandardowe klasy wyjątków, można efektywnie zarządzać błędami w API. Metodologia ta umożliwia oddzielenie obsługi błędów od zawiłości implementacji API, ułatwiając w ten sposób dodawanie unikalnych wyjątków lub zmian w miarę postępów API lub konfrontacji z nowymi scenariuszami błędów.

Zawijanie niestandardowych wyjątków

Zawijanie wyjątków obejmuje przechwycenie wyjątku, otoczenie go dostosowanym wyjątkiem i podniesienie tego nowego wyjątku wraz z identyfikacją oryginalnego wyjątku jako jego podstawową przyczyną. Takie podejście służy do zapewnienia kontekstu dla komunikatów o błędach i ukrywa specyfikę implementacji przed kodem wywołującym.

W przypadku, gdy aplikacja internetowa komunikuje się z interfejsem API i napotyka błąd LookupError , możliwe jest przechwycenie tego błędu za pomocą obsługi wyjątków. Następnie można wygenerować niestandardowy wyjątek APINotFoundError , który służy jako klasa pochodna od oryginalnej klasy Exception w Pythonie. Ten nowy wyjątek może zawierać początkowy LookupError jako podstawowy czynnik przyczynowy, zapewniając w ten sposób dodatkowy kontekst dotyczący konkretnego problemu.

 def request_api():
    try:
        # Simulate an API error for demonstration purposes
        # Assuming the external API raised a LookupError
        raise LookupError("Sorry, You Encountered A LookUpError !!!")
    except LookupError as original_exception:
        try:
            # Wrap the original exception with a custom exception
            raise APINotFoundError \
                 ("Requested resource not found.") from original_exception
        except APINotFoundError as wrapped_exception:
            # Handle the wrapped exception here
             print(f"Caught wrapped API exception: {wrapped_exception}")

            # or re-raise it if necessary
            raise

try:
    request_api()
except APINotFoundError as err:
    print(f"Caught API exception: {err.__cause__}")

Użyj klauzuli “from” w połączeniu z instrukcją “raise”, aby nawiązać do początkowego wyjątku w dostosowanym wyjątku.

/pl/images/wrapping-custom-exception.jpg

Wyjątek niestandardowy zawiera oryginalny wyjątek jako swoją podstawową przyczynę, nadając mu związek przyczynowy, który można prześledzić wstecz do źródła problemu. Badając ten atrybut, można dostrzec pochodzenie wszelkich rozbieżności lub anomalii napotkanych podczas wykonywania.

Otaczanie wyjątków pozwala na dostarczenie bardziej merytorycznego kontekstu przy jednoczesnym przekazywaniu bardziej istotnych powiadomień o błędach do użytkowników końcowych, bez ujawniania skomplikowanego działania kodu lub interfejsu API. Ponadto ułatwia organizację i rozwiązywanie różnych typów błędów w uporządkowany i spójny sposób.

Dostosowywanie zachowania klas w Pythonie

Poprzez dziedziczenie bazowej klasy wyjątków dostarczanej przez Pythona, można opracować podstawowe, ale skuteczne wyjątki, które będą zgłaszane podczas wystąpienia określonych błędów w kodzie. Co więcej, poprzez implementację metod magicznych lub “dunder”, można ustanowić niestandardowe zachowania dla własnych klas wyjątków.