Qual è il motivo per cui esiste try-tranne-else?
Un try
blocco consente di gestire un errore previsto. Il except
blocco dovrebbe rilevare solo le eccezioni che sei pronto a gestire. Se gestisci un errore imprevisto, il tuo codice potrebbe fare la cosa sbagliata e nascondere i bug.
Una else
clausola verrà eseguita se non ci sono errori e, non eseguendo quel codice nel try
blocco, si evita di rilevare un errore imprevisto. Ancora una volta, la cattura di un errore imprevisto può nascondere i bug.
Esempio
Per esempio:
try:
try_this(whatever)
except SomeException as the_exception:
handle(the_exception)
else:
return something
La suite "provare, tranne" ha due clausole opzionali, else
e finally
. Quindi in realtà lo è try-except-else-finally
.
else
valuterà solo se non vi è alcuna eccezione dal try
blocco. Ci consente di semplificare il codice più complicato di seguito:
no_error = None
try:
try_this(whatever)
no_error = True
except SomeException as the_exception:
handle(the_exception)
if no_error:
return something
quindi se confrontiamo un else
un'alternativa (che potrebbe creare bug) vediamo che riduce le righe di codice e possiamo avere una base di codice più leggibile, gestibile e meno buggy.
finally
finally
verrà eseguito indipendentemente da ciò, anche se un'altra riga viene valutata con un'istruzione return.
Suddiviso con pseudo-codice
Potrebbe aiutare a scomporlo, nella forma più piccola possibile che dimostra tutte le funzionalità, con commenti. Supponiamo che questo pseudo-codice sia sintatticamente corretto (ma non eseguibile a meno che non vengano definiti i nomi) in una funzione.
Per esempio:
try:
try_this(whatever)
except SomeException as the_exception:
handle_SomeException(the_exception)
# Handle a instance of SomeException or a subclass of it.
except Exception as the_exception:
generic_handle(the_exception)
# Handle any other exception that inherits from Exception
# - doesn't include GeneratorExit, KeyboardInterrupt, SystemExit
# Avoid bare `except:`
else: # there was no exception whatsoever
return something()
# if no exception, the "something()" gets evaluated,
# but the return will not be executed due to the return in the
# finally block below.
finally:
# this block will execute no matter what, even if no exception,
# after "something" is eval'd but before that value is returned
# but even if there is an exception.
# a return here will hijack the return functionality. e.g.:
return True # hijacks the return in the else clause above
E 'vero che abbiamo potuto includere il codice nel else
blocco nel try
blocco, invece, dove sarebbe eseguito se non ci fossero delle eccezioni, ma cosa succede se quel codice si solleva un'eccezione del genere stiamo cattura? Lasciandolo nel try
blocco nasconderebbe quel bug.
Vogliamo ridurre al minimo le righe di codice nel try
blocco per evitare di rilevare eccezioni che non ci aspettavamo, in base al principio che se il nostro codice fallisce, vogliamo che fallisca ad alta voce. Questa è una buona pratica .
Comprendo che le eccezioni non sono errori
In Python, la maggior parte delle eccezioni sono errori.
Possiamo visualizzare la gerarchia delle eccezioni usando pydoc. Ad esempio, in Python 2:
$ python -m pydoc exceptions
o Python 3:
$ python -m pydoc builtins
Ci darà la gerarchia. Possiamo vedere che la maggior parte dei tipi di Exception
errori sono, sebbene Python ne usi alcuni per cose come la fine di for
loop ( StopIteration
). Questa è la gerarchia di Python 3:
BaseException
Exception
ArithmeticError
FloatingPointError
OverflowError
ZeroDivisionError
AssertionError
AttributeError
BufferError
EOFError
ImportError
ModuleNotFoundError
LookupError
IndexError
KeyError
MemoryError
NameError
UnboundLocalError
OSError
BlockingIOError
ChildProcessError
ConnectionError
BrokenPipeError
ConnectionAbortedError
ConnectionRefusedError
ConnectionResetError
FileExistsError
FileNotFoundError
InterruptedError
IsADirectoryError
NotADirectoryError
PermissionError
ProcessLookupError
TimeoutError
ReferenceError
RuntimeError
NotImplementedError
RecursionError
StopAsyncIteration
StopIteration
SyntaxError
IndentationError
TabError
SystemError
TypeError
ValueError
UnicodeError
UnicodeDecodeError
UnicodeEncodeError
UnicodeTranslateError
Warning
BytesWarning
DeprecationWarning
FutureWarning
ImportWarning
PendingDeprecationWarning
ResourceWarning
RuntimeWarning
SyntaxWarning
UnicodeWarning
UserWarning
GeneratorExit
KeyboardInterrupt
SystemExit
Un commentatore ha chiesto:
Supponi di avere un metodo che esegue il ping di un'API esterna e desideri gestire l'eccezione in una classe esterna al wrapper dell'API, restituisci semplicemente e dal metodo nella clausola salvo dove e è l'oggetto dell'eccezione?
No, non si restituisce l'eccezione, ma si rialza nuovamente con uno bare raise
per preservare lo stack.
try:
try_this(whatever)
except SomeException as the_exception:
handle(the_exception)
raise
Oppure, in Python 3, puoi sollevare una nuova eccezione e preservare il backtrace con il concatenamento delle eccezioni:
try:
try_this(whatever)
except SomeException as the_exception:
handle(the_exception)
raise DifferentException from the_exception
Ho elaborato nella mia risposta qui .