Benutzerdefinierte Ausnahmen

In Python stellen Ausnahmen eine Möglichkeit dar, auf Fehler zu reagieren, die während der Ausführung eines Programms auftreten können. Python verfügt über zahlreiche eingebaute Ausnahmen wie IndexError, wenn Sie versuchen, auf einen Index zuzugreifen, der in einer Liste nicht existiert, oder TypeError, wenn Sie versuchen, eine Operation auf einem Typ auszuführen, der sie nicht unterstützt. Manchmal kann es jedoch nützlich sein, eigene Ausnahmen zu definieren. Dies ermöglicht es uns, aussagekräftige Fehlermeldungen zu geben und auf spezifische Situationen zu reagieren, die die eingebauten Ausnahmen nicht abdecken können. Benutzerdefinierte Ausnahmen werden erstellt, indem eine neue Klasse definiert wird, die von der eingebauten Exception-Klasse oder einer ihrer Unterklassen erbt.
Betrachten wir ein Programm, das mit einem theoretischen Bibliothekssystem arbeitet. In diesem System sollte es einem Benutzer nicht erlaubt sein, mehr als 5 Bücher gleichzeitig auszuleihen. So könnten wir eine benutzerdefinierte Ausnahme dafür erstellen:
class TooManyBooksError(Exception):  # Definieren einer neuen Ausnahmeklasse
    pass

def checkout_books(user, num_books):
    if num_books > 5:  
        # Eine Ausnahme auslösen, wenn der Benutzer versucht, mehr als 5 Bücher auszuleihen
        raise TooManyBooksError('You cannot check out more than 5 books at a time.')  # Unsere benutzerdefinierte Ausnahme auslösen
    # Andernfalls die Bücher dem Benutzerkonto hinzufügen.
    user.books += num_books
Im obigen Beispiel wird, wenn ein Benutzer versucht, mehr als 5 Bücher gleichzeitig auszuleihen, unsere benutzerdefinierte TooManyBooksError mit einer spezifischen Fehlermeldung ausgelöst. Wenn die Ausnahme nicht abgefangen wird, stoppt das Programm die Ausführung und die Fehlermeldung wird auf der Konsole ausgegeben.
Wir können diese Ausnahme auf die gleiche Weise wie eingebaute Ausnahmen mit einem try/except-Block abfangen:
try:
    checkout_books(user, 7)     # Versuchen, 7 Bücher auszuleihen.
except TooManyBooksError as e:  # Unsere benutzerdefinierte Ausnahme abfangen
    print(e)                    # Die Fehlermeldung ausgeben.
Wenn die Zeile checkout_books(user, 7) eine TooManyBooksError auslöst, wird das Programm sie abfangen und unsere Fehlermeldung 'You cannot check out more than 5 books at a time.' ausgeben. Auf diese Weise können wir den Fehler elegant handhaben und das Programm kann weiterlaufen.
Wie bei jeder anderen Klasse können wir unserer Ausnahmeklasse Methoden hinzufügen. Zum Beispiel fügen wir eine Methode hinzu, die berechnet, wie viele Bücher der Benutzer zurückgeben muss, um die gewünschte Anzahl auszuleihen:
class TooManyBooksError(Exception):  
    def __init__(self, attempted, limit=5, checked_out=0):  
        self.attempted = attempted
        self.limit = limit
        self.checked_out = checked_out
        self.message = f'You tried to check out {self.attempted} books, but the limit is {self.limit} books.'
        super().__init__(self.message)
        
    def books_to_return(self):
        return self.checked_out + self.attempted - self.limit
Wir können diese Methode nach dem Abfangen der Ausnahme verwenden:
try:
    checkout_books(user, 7)
except TooManyBooksError as e:
    print(e)                                                   # Sie haben versucht, 7 Bücher auszuleihen, aber das Limit beträgt 5 Bücher.
    print(f'Sie müssen {e.books_to_return()} Bücher zurückgeben.')  # Sie müssen 2 Bücher zurückgeben.
Benutzerdefinierte Ausnahmen können also ihre eigenen Attribute und Methoden haben, was sie zu einem leistungsstarken Werkzeug macht, um Fehler auf eine Weise zu handhaben, die speziell auf die Bedürfnisse Ihres Programms zugeschnitten ist.
 

Beste Praktiken bei der Definition benutzerdefinierter Ausnahmen

Wenn Sie benutzerdefinierte Ausnahmen erstellen, neigen Sie oft dazu, eine leere Klasse zu definieren, die von Exception erbt, und dann jedes Mal, wenn Sie die Ausnahme auslösen, die gesamte Fehlermeldung als String zu übergeben. Obwohl dieser Ansatz funktioniert, kann er zu wiederholtem Code führen und es erschweren, Ihre Ausnahme-Meldungen konsistent zu halten.
Stattdessen ist es eine bessere Praxis, alle dynamischen Daten in der Ausnahmeklasse zu speichern. So können Sie die Fehlermeldung intern aufbauen und Ihren Code sauberer und besser organisiert halten:
👎 Schlechte Praxis – Die gesamte Nachricht jedes Mal übergeben:
class TooManyBooksError(Exception):
    pass

# Sie sind gezwungen, die Meldung jedes Mal zu übergeben
raise TooManyBooksError('You tried to check out 7 books, but the limit is 5!')

# Das ist langfristig eine schlechte Idee
👍 Ein besserer Ansatz – Die dynamischen Details an die Ausnahmeklasse übergeben:
class TooManyBooksError(Exception):
    def __init__(self, attempted, limit=5):
        self.attempted = attempted
        self.limit = limit
        # Erstellen der Fehlermeldung aus den Daten
        self.message = f'You tried to check out {self.attempted} books, but the limit is {self.limit}.'
        super().__init__(self.message)

# Beim Auslösen der Ausnahme nur die relevanten Daten übergeben
raise TooManyBooksError(7)
💡
Indem Sie die Ausnahmeklasse die Erstellung der Fehlermeldung übernehmen lassen, vermeiden Sie Wiederholungen. Wenn Sie die Formulierung oder das Format der Meldung ändern müssen, brauchen Sie dies nur an einer Stelle zu tun—innerhalb der __init__-Methode Ihrer Ausnahmeklasse.
 

Herausforderung: Altersdatenbank

In einer Stadt, in der jeder jeden kennt, versucht die lokale Regierung, ein Alterskontrollsystem in städtischen Einrichtungen wie Bars, Clubs und Theatern zu implementieren. Sie benötigen ein Programm, das, gegeben ein Wörterbuch mit den Altersangaben der Personen, einen Namen akzeptiert und das Alter dieser Person zurückgibt.
Die Eingabe wird automatisch gehandhabt. Sie sollen die Funktion find_age(name) implementieren, die bei Angabe eines Namens das Alter der Person zurückgibt.
Allerdings sind nicht alle Personen in der städtischen Datenbank erfasst. Wenn das Programm einen Namen in der Datenbank nicht finden kann, soll es eine benutzerdefinierte Ausnahme namens NameNotFoundError auslösen und eine entsprechende Fehlermeldung ausgeben: {name} not found in the database. Achten Sie darauf, die Meldung innerhalb der NameNotFoundError-Klasse zu erstellen.
Input
Output
5 Alice - 24 Bob - 32 Charlie - 21 Daisy - 27 Edgar - 30 Edgar
Edgar - 30
5 Alice - 24 Bob - 32 Charlie - 21 Daisy - 27 Edgar - 30 George
NameNotFoundError: George not found in the database.
Hinweis: Namen und Alter sind case-sensitiv und können Leerzeichen enthalten. Der eingegebene Name muss genau dem Namen in der Datenbank entsprechen.
 

Constraints

Time limit: 2 seconds

Memory limit: 512 MB

Output limit: 1 MB

To check your solution you need to sign in
Sign in to continue