Eccezioni Personalizzate

In Python, le eccezioni sono un modo per gestire gli errori che possono verificarsi durante l'esecuzione di un programma. Python ha numerose eccezioni predefinite come IndexError quando si tenta di accedere a un indice che non esiste in una lista, o TypeError quando si cerca di eseguire un'operazione su un tipo che non la supporta. A volte, tuttavia, può essere utile definire le nostre eccezioni. Questo ci permette di fornire messaggi di errore significativi e di rispondere a situazioni specifiche che le eccezioni predefinite non possono gestire. Le eccezioni personalizzate vengono create definendo una nuova classe che eredita dalla classe incorporata Exception o una delle sue sottoclassi.
Consideriamo un programma che lavora con un sistema bibliotecario teorico. In questo sistema, un utente non dovrebbe poter prendere in prestito più di 5 libri alla volta. Ecco come potremmo creare un'eccezione personalizzata per questo:
class TooManyBooksError(Exception):  # Definisce una nuova classe di eccezione
    pass

def checkout_books(user, num_books):
    if num_books > 5:  
        # Solleva un'eccezione se l'utente tenta di prendere in prestito più di 5 libri
        raise TooManyBooksError('You cannot check out more than 5 books at a time.')  # Solleva la nostra eccezione personalizzata
    # Altrimenti, aggiunge i libri all'account dell'utente.
    user.books += num_books
Nell'esempio precedente, se un utente tenta di prendere in prestito più di 5 libri alla volta, verrà sollevata la nostra eccezione personalizzata TooManyBooksError con un messaggio di errore specifico. Se l'eccezione non viene intercettata, il programma interromperà l'esecuzione e il messaggio di errore verrà stampato in console.
Possiamo intercettare questa eccezione allo stesso modo delle eccezioni predefinite utilizzando un blocco try / except:
try:
    checkout_books(user, 7)     # Tenta di prendere in prestito 7 libri.
except TooManyBooksError as e:  # Intercetta la nostra eccezione personalizzata
    print(e)                    # Stampa il messaggio di errore.
Se la linea checkout_books(user, 7) solleva un TooManyBooksError, il programma lo intercetterà e stamperà il nostro messaggio di errore 'You cannot check out more than 5 books at a time.'. In questo modo, possiamo gestire l'errore in modo elegante e il programma può continuare a funzionare.
Come per qualsiasi altra classe, possiamo aggiungere metodi alla nostra classe di eccezione. Ad esempio, aggiungiamo un metodo che calcola quanti libri l'utente deve restituire per poter prendere in prestito la quantità desiderata:
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
Possiamo usare questo metodo dopo aver intercettato l'eccezione:
try:
    checkout_books(user, 7)
except TooManyBooksError as e:
    print(e)                                                   # Hai provato a prendere in prestito 7 libri, ma il limite è 5 libri.
    print(f'You need to return {e.books_to_return()} books.')  # Devi restituire 2 libri.
Quindi, le eccezioni personalizzate possono avere i propri attributi e metodi, rendendole uno strumento potente per gestire gli errori in modo personalizzato secondo le esigenze specifiche del tuo programma.
 

Buone Pratiche nella Definizione di Eccezioni Personalizzate

Quando si creano eccezioni personalizzate, si può essere tentati di definire una classe vuota che eredita da Exception, per poi passare l'intero messaggio di errore come stringa ogni volta che si solleva l'eccezione. Sebbene questo approccio funzioni, può portare a codice ripetitivo e rende più difficile mantenere i messaggi di eccezione coerenti.
Invece, una pratica migliore è memorizzare qualsiasi dato dinamico nella classe di eccezione. In questo modo, puoi costruire il messaggio dell'eccezione internamente, rendendo il tuo codice più pulito e organizzato:
👎 Cattiva pratica – passare l'intero messaggio ogni volta:
class TooManyBooksError(Exception):
    pass

# Sei costretto a passare il messaggio ogni volta
raise TooManyBooksError('You tried to check out 7 books, but the limit is 5!')

# Questa è una cattiva idea a lungo termine
👍 Una soluzione migliore – passare i dettagli dinamici alla classe di eccezione:
class TooManyBooksError(Exception):
    def __init__(self, attempted, limit=5):
        self.attempted = attempted
        self.limit = limit
        # Costruisci il messaggio di errore dai dati
        self.message = f'You tried to check out {self.attempted} books, but the limit is {self.limit}.'
        super().__init__(self.message)

# Quando sollevi l'eccezione, passa solo i dati pertinenti
raise TooManyBooksError(7)
💡
Lasciando che la classe di eccezione gestisca la creazione del messaggio di errore, eviti di ripeterti. Se hai bisogno di cambiare la formulazione o la formattazione del messaggio, devi farlo in un solo posto—nel metodo __init__ della tua eccezione.
 

Sfida: Database delle Età

In una città dove tutti conoscono tutti, il governo locale sta cercando di implementare un sistema di verifica dell'età nei locali cittadini come bar, club e teatri. Hanno bisogno di un programma che possa, dato un dizionario delle età delle persone, accettare un nome e restituire l'età di quella persona.
L'input è gestito automaticamente. Dovresti implementare la funzione find_age(name) che, dato un nome, restituisce l'età della persona.
Tuttavia, non tutte le persone potrebbero essere nel database della città. Se il programma non riesce a trovare un nome nel database, dovrebbe sollevare un'eccezione personalizzata chiamata NameNotFoundError e stampare un messaggio di errore appropriato: {name} not found in the database. Assicurati di creare il messaggio all'interno della classe NameNotFoundError.
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.
Nota: Nomi ed età fanno distinzione tra maiuscole e minuscole e possono contenere spazi. Il nome inserito deve corrispondere esattamente al nome nel database.
 

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