Come hai indovinato correttamente, ci sono due lati: catturare qualsiasi errore specificando dopo nessun tipo di eccezione except
e semplicemente passandolo senza intraprendere alcuna azione.
La mia spiegazione è "un po 'più lunga", quindi tl; dr si scompone in questo modo:
- Non cogliere alcun errore . Specifica sempre da quali eccezioni sei pronto a recuperare e cattura solo quelle.
- Cerca di evitare di passare tranne i blocchi . Se non esplicitamente desiderato, questo di solito non è un buon segno.
Ma andiamo nel dettaglio:
Non cogliere alcun errore
Quando si utilizza un try
blocco, di solito lo si fa perché si sa che esiste la possibilità che venga generata un'eccezione. Pertanto, hai già un'idea approssimativa di ciò che può rompersi e quale eccezione può essere lanciata. In tali casi, si rileva un'eccezione perché è possibile recuperarlo positivamente . Ciò significa che sei preparato per l'eccezione e hai un piano alternativo che seguirai in caso di tale eccezione.
Ad esempio, quando si chiede all'utente di inserire un numero, è possibile convertire l'input utilizzando int()
quale potrebbe sollevare un ValueError
. Puoi facilmente recuperarlo semplicemente chiedendo all'utente di riprovare, quindi catturare ValueError
e sollecitare nuovamente l'utente sarebbe un piano appropriato. Un esempio diverso potrebbe essere se si desidera leggere alcune configurazioni da un file e quel file non esiste. Poiché si tratta di un file di configurazione, potresti avere una configurazione predefinita come fallback, quindi il file non è esattamente necessario. Quindi catturare un FileNotFoundError
e semplicemente applicare la configurazione predefinita sarebbe un buon piano qui. Ora in entrambi questi casi, abbiamo un'eccezione molto specifica che ci aspettiamo e abbiamo un piano altrettanto specifico per riprenderci. Come tale, in ogni caso, abbiamo esplicitamente solo except
questo eccezione.
Tuttavia, se dovessimo catturare tutto , allora - oltre a quelle eccezioni da cui siamo pronti a riprenderci - c'è anche la possibilità che otteniamo eccezioni che non ci aspettavamo e da cui non possiamo effettivamente recuperare; o non dovrebbe riprendersi da.
Facciamo l'esempio del file di configurazione dall'alto. In caso di file mancante, abbiamo appena applicato la nostra configurazione predefinita e potremmo decidere in un secondo momento di salvare automaticamente la configurazione (quindi la prossima volta il file esiste). Ora immagina di ottenere un IsADirectoryError
, o unPermissionError
anziché. In tali casi, probabilmente non vogliamo continuare; potremmo ancora applicare la nostra configurazione predefinita, ma in seguito non saremo in grado di salvare il file. Ed è probabile che anche l'utente intendesse avere una configurazione personalizzata, quindi è probabile che non si desideri utilizzare i valori predefiniti. Quindi vorremmo parlarne immediatamente all'utente e probabilmente interrompere anche l'esecuzione del programma. Ma non è qualcosa che vogliamo fare da qualche parte in profondità all'interno di una piccola parte di codice; questo è qualcosa di importante a livello di applicazione, quindi dovrebbe essere gestito in alto, quindi lasciate che l'eccezione si gonfi.
Un altro semplice esempio è anche menzionato nel documento di idiomi di Python 2 . Qui, nel codice esiste un semplice errore di battitura che provoca l'interruzione. Poiché stiamo rilevando ogni eccezione, rileviamo anche NameError
s e SyntaxError
s . Entrambi sono errori che accadono a tutti noi durante la programmazione; ed entrambi sono errori che non vogliamo assolutamente includere quando spediamo il codice. Ma poiché abbiamo anche catturato quelli, non sapremo nemmeno che si sono verificati lì e perderebbero qualsiasi aiuto per eseguire il debug correttamente.
Ma ci sono anche eccezioni più pericolose per le quali probabilmente non siamo preparati. Ad esempio SystemError è di solito qualcosa che accade raramente e che non possiamo davvero pianificare; significa che sta succedendo qualcosa di più complicato, qualcosa che probabilmente ci impedisce di continuare l'attività corrente.
In ogni caso, è molto improbabile che tu sia preparato a tutto in una piccola parte del codice, quindi è lì che dovresti solo catturare le eccezioni per le quali sei preparato. Alcune persone suggeriscono di catturare almeno Exception
perché non includerà cose come SystemExit
e KeyboardInterrupt
che, di progettazione, devono chiudere l'applicazione, ma direi che questo è ancora troppo poco specifico. C'è solo un posto in cui accetto personalmente la cattura Exception
o semplicemente qualsiasieccezione, vale a dire in un unico gestore di eccezioni a livello di applicazione globale che ha il solo scopo di registrare qualsiasi eccezione per la quale non eravamo preparati. In questo modo, possiamo ancora conservare quante più informazioni sulle eccezioni impreviste, che possiamo quindi utilizzare per estendere il nostro codice per gestirle esplicitamente (se riusciamo a recuperarle) o, in caso di un bug, per creare casi di test per assicurarci non succederà più. Ma, naturalmente, funziona solo se abbiamo colto solo le eccezioni che già ci aspettavamo, quindi quelle che non ci aspettavamo salteranno naturalmente.
Cerca di evitare di passare tranne i blocchi
Quando si coglie esplicitamente una piccola selezione di eccezioni specifiche, ci sono molte situazioni in cui andremo bene semplicemente non facendo nulla. In questi casi, solo avere except SomeSpecificException: pass
va bene. Il più delle volte, tuttavia, non è così, poiché probabilmente abbiamo bisogno di un po 'di codice relativo al processo di recupero (come menzionato sopra). Questo può essere, ad esempio, qualcosa che ritenta nuovamente l'azione, o invece impostare un valore predefinito.
In caso contrario, ad esempio perché il nostro codice è già strutturato per essere ripetuto fino a quando non ha esito positivo, il passaggio è sufficiente. Prendendo il nostro esempio dall'alto, potremmo voler chiedere all'utente di inserire un numero. Poiché sappiamo che agli utenti piace non fare ciò che chiediamo loro, potremmo semplicemente metterlo in un ciclo in primo luogo, quindi potrebbe apparire così:
def askForNumber ():
while True:
try:
return int(input('Please enter a number: '))
except ValueError:
pass
Poiché continuiamo a provare fino a quando non viene generata alcuna eccezione, non abbiamo bisogno di fare nulla di speciale nel blocco tranne, quindi va bene. Ma, naturalmente, si potrebbe sostenere che almeno vogliamo mostrare all'utente un messaggio di errore per dirgli perché deve ripetere l'input.
In molti altri casi, tuttavia, il semplice passaggio di un except
segno indica che non eravamo davvero preparati all'eccezione che stiamo rilevando. A meno che tali eccezioni non siano semplici (come ValueError
o TypeError
) e il motivo per cui possiamo passare sia ovvio, cerca di evitare di passare. Se non c'è davvero niente da fare (e ne sei assolutamente sicuro), allora considera di aggiungere un commento perché è così; in caso contrario, espandere il blocco di esclusione per includere effettivamente un codice di ripristino.
except: pass
Il peggior trasgressore però è la combinazione di entrambi. Ciò significa che stiamo rilevando volontariamente qualsiasi errore, anche se non siamo assolutamente preparati per questo e non facciamo nulla al riguardo. È almeno desidera registrare l'errore e anche probabile che reraise per terminare ancora l'applicazione (è improbabile è possibile continuare come normale dopo un MemoryError). Il solo passaggio non solo manterrà l'applicazione in qualche modo viva (a seconda di dove catturi, ovviamente), ma eliminerà anche tutte le informazioni, rendendo impossibile scoprire l'errore, il che è particolarmente vero se non sei tu a scoprirlo.
Quindi la linea di fondo è: prendi solo le eccezioni che davvero ti aspetti e da cui sei pronto a recuperare; tutti gli altri sono probabilmente errori che dovresti correggere o comunque qualcosa per cui non sei preparato. Passare eccezioni specifiche va bene se davvero non hai bisogno di fare qualcosa al riguardo. In tutti gli altri casi, è solo un segno di presunzione ed essere pigri. E sicuramente vuoi risolverlo.
logging
modulo a livello di DEBUG per evitarli di eseguire lo streaming in produzione, ma tenerli disponibili in fase di sviluppo.