È meglio avere molte eccezioni specificate o alcuni generali generati con una descrizione specifica?

0

Qual è il modo migliore per organizzare l'eccezione in un progetto Python? Qual è il modo giusto di usare la descrizione dell'eccezione?

Ad esempio, ho una funzione che analizza la posta elettronica e restituisce alcuni dati dal suo allegato. Dovrebbe generare un'eccezione che specifichi e spieghi un errore in un modo migliore.

Opzioni:

  1. Utilizza un'eccezione generale per un processo e il suo messaggio per specificare l'errore:

    class ParseMailError(Exception):
        pass

    def get_parsed_mail(mail):
        if not mail.attachment:
            raise ParseMailError('No attachment')

        if not check_some_necessary_fields_format(mail):
            raise ParseMailError('Invalid attachment field format')

        ...
  1. Per ogni caso, crea un'eccezione:

    class ParseMailError(Exception):
        pass

    class MailNoAttachmentError(ParseMailError):
        pass

    class MailInvalidFieldError(ParseMailError):
        pass

    def get_parsed_mail(mail):
        if not mail.attachment:
            raise MailNoAttachmentError

        if not check_some_necessary_fields_format(mail):
            raise MailInvalidFieldError

        ...

C'è qualche spiegazione su come renderlo migliore?

    
posta Yann 01.03.2017 - 19:02
fonte

5 risposte

1

Il rasoio di Occam:

More things should not be used than are necessary.

Python ti dà la possibilità di creare sottoclassi di eccezioni. Quella estensibilità è grande, quando ne hai bisogno. Ma è raro che tu abbia bisogno di farlo. Spesso è possibile ottenere lo stesso errore segnalando la chiarezza attraverso la personalizzazione del messaggio di un tipo di eccezione standard. Per esempio:.

def get_parsed_mail(mail):
    if not mail.attachment:
        raise ValueError('No attachment')

    if not check_some_necessary_fields_format(mail):
        raise ValueError('Invalid attachment field format')

Le eccezioni standard appartengono a una gerarchia ben strutturata e collaudata che molti sviluppatori hanno studiato e più o meno capiscono.

Le eccezioni personalizzate aiutano a segnalare il modulo che ha generato l'errore ("questo errore proviene da MailParser !") e il suo tipo, ma spesso tali informazioni sono eccessive. Prendere in considerazione:

try:
    mail_parts = ParseMail.parse(raw_message)
except Exception as e:
    ...

Visto nel contesto in cui verrà usato, la fonte non è un mistero. Sapete dal contesto che l'errore proviene da ParseMail . Sai che è un MailNoAttachmentError personalizzato o semplicemente un ValueError .

Oltre alla fonte, diversi tipi di eccezione possono aiutarti a capire il tipo di errore. Cosa è successo, esattamente? La vera domanda è, distinguere quei tipi aiuta a rispondere alle condizioni di errore? Ti aspetti che ci siano gestori di errori per ognuno di questi diversi tipi di errore?

try:
    mail_parts = ParseMail.parse(raw_message)
except MailNoAttachmentError as e:
    ...
except MailInvalidFieldError as e:
    ...

C'è qualcosa che un programma può fare per correggere quei singoli casi? O stai semplicemente segnalando allo sviluppatore / utente? Se si sta solo riportando a un essere umano, la personalizzazione del messaggio dell'eccezione è importante almeno quanto il tipo dell'eccezione. Probabilmente di più.

Quindi eccezioni personalizzate sono solo occasionalmente necessarie. Piggybacking di eccezioni standard, ma utilizzando i propri messaggi personalizzati, generalmente funziona altrettanto bene ed è più semplice.

Ma se ritieni che ci sia virtù in segnali di sorgente / tipo di errore più precisi, va bene anche questo. Un singolo tipo di errore personalizzato, solitamente derivato da un errore comune come ValueError , è sufficiente per la maggior parte dei moduli, ad esempio:

class ParseMailError(ValueError):
    pass

Se si desidera ottenere un'eccezione / precisione delle eccezioni Java-esque completa, è possibile creare la propria gerarchia di eccezioni ParseMail unita alla gerarchia standard attraverso l'ereditarietà multipla:

class ParseMailError(Exception):
    pass

class MailNoAttachmentError(ValueError, ParseMailError):
    pass

class MailInvalidFieldError(TypeError, ParseMailError):
    pass   
    
risposta data 01.03.2017 - 20:32
fonte
5

Una tipica buona pratica è quella di avere una classe di eccezione "ombrello" per libreria / modulo / area tematica e avere eccezioni più specifiche come sottoclassi di essa:

CarException  # The umbrella class
  NoFuelException
  FlatWheelException
  EngineException
    SparkPlugException  # You can get as specific as you want

I vantaggi:

  • I clienti sono sicuri che catturino tutte le eccezioni che la tua libreria car può generare se catturano CarException .
  • I client sono liberi di rilevare eccezioni più specifiche, se lo desiderano, e ottenere dettagli specifici per il problema.
  • Gli autori delle librerie possono aggiungere nuove eccezioni o rendere più dettagliate le eccezioni esistenti, senza violare il codice client esistente.
risposta data 01.03.2017 - 19:43
fonte
1

Anche se generalmente si tratta di una situazione caso per caso, mi piacciono i suggerimenti forniti sul sito ufficiale di C ++: link

Naturalmente, quelle opinioni sono su misura per C ++ e non per Python, ma molti concetti sono ancora rilevanti. In particolare (sottolineatura mia):

Here are some “wrong exception-handling mindsets” in no apparent order:

...

Organizing the exception classes around the physical thrower rather than the logical reason for the throw: For example, in a banking app, suppose any of five subsystems might throw an exception when the customer has insufficient funds. The right approach is to throw an exception representing the reason for the throw, e.g., an “insufficient funds exception”; the wrong mindset is for each subsystem to throw a subsystem-specific exception.

    
risposta data 01.03.2017 - 22:04
fonte
0

Non esiste un modo "migliore" per farlo, tutto dipende da ciò che stai cercando di ottenere.

dai tuoi esempi, se l'unica differenza è il testo, che resta fedele a #1 se hai una logica diversa per gestire ciascun caso, allora considera di andare con #2

tra le due opzioni, penso che #2 sia più pulito, renderà le tue funzioni più leggibili, un potenziale di riusabilità molto migliore e manterrà un design migliore per miglioramenti futuri a lungo termine. Ma solo se pianifichi di fare altre cose oltre a passare messaggi di testo diversi, andrò da questa parte.

mentre #1 è una soluzione molto più "locale" e richiederà una maggiore manutenzione se è necessaria una modifica o se è necessario aggiungere più funzionalità.

Potresti iniziare con #1 e passare a #2 .

spero che sia stato d'aiuto

    
risposta data 01.03.2017 - 19:27
fonte
0

Considera che hai un compito che potrebbe fallire per un numero di motivi previsti. Dovresti creare e usare un nuovo tipo di eccezione per quello. Dovresti racchiudere (chiamare) l'attività in un gestore di eccezioni che gestisce il tuo tipo di eccezione.

Questa è la regola. Ora tutto dipende dalla granularità che trovi utile e / o conveniente. Quali compiti riconosci? Cosa consideri essere "una cosa da fare"?

La domanda può tornare su più livelli nella tua applicazione.

Quindi dipende molto dalle attività che vuoi riconoscere.

    
risposta data 01.03.2017 - 22:32
fonte

Leggi altre domande sui tag