Python Language
eccezioni
Ricerca…
introduzione
Gli errori rilevati durante l'esecuzione sono chiamati eccezioni e non sono incondizionatamente fatali. La maggior parte delle eccezioni non sono gestite dai programmi; è possibile scrivere programmi che gestiscono eccezioni selezionate. Ci sono funzioni specifiche in Python per gestire le eccezioni e la logica delle eccezioni. Inoltre, le eccezioni hanno una gerarchia di tipo ricco, che eredita dal tipo BaseException
.
Sintassi
- aumentare l' eccezione
- raise # re-raise un'eccezione che è già stata sollevata
- genera un'eccezione dalla causa # Python 3 - imposta la causa dell'eccezione
- genera un'eccezione da None # Python 3 - sopprime tutto il contesto di eccezione
- provare:
- tranne [tipi di eccezioni] [ come identificatore ] :
- altro:
- finalmente:
Sollevare le eccezioni
Se il tuo codice rileva una condizione che non sa come gestire, come un parametro errato, dovrebbe sollevare l'eccezione appropriata.
def even_the_odds(odds):
if odds % 2 != 1:
raise ValueError("Did not get an odd number")
return odds + 1
Cattura le eccezioni
Usa try...except:
per rilevare eccezioni. Dovresti specificare un'eccezione precisa che puoi:
try:
x = 5 / 0
except ZeroDivisionError as e:
# `e` is the exception object
print("Got a divide by zero! The exception was:", e)
# handle exceptional case
x = 0
finally:
print "The END"
# it runs no matter what execute.
La classe di eccezione specificata - in questo caso ZeroDivisionError
- ZeroDivisionError
qualsiasi eccezione che sia di quella classe o di qualsiasi sottoclasse di tale eccezione.
Ad esempio, ZeroDivisionError
è una sottoclasse di ArithmeticError
:
>>> ZeroDivisionError.__bases__
(<class 'ArithmeticError'>,)
E così, il seguente catturerà ancora l' ZeroDivisionError
:
try:
5 / 0
except ArithmeticError:
print("Got arithmetic error")
Esecuzione del codice di pulizia con finalmente
A volte, è possibile che si verifichi che qualcosa si verifichi indipendentemente dall'eccezione che si è verificata, ad esempio, se è necessario ripulire alcune risorse.
La finally
del blocco di un try
clausola di accadrà indipendentemente dal fatto che le eccezioni sono state sollevate.
resource = allocate_some_expensive_resource()
try:
do_stuff(resource)
except SomeException as e:
log_error(e)
raise # re-raise the error
finally:
free_expensive_resource(resource)
Questo schema è spesso gestito meglio con i gestori di contesto (usando l'istruzione with
).
Risollevare le eccezioni
A volte si desidera rilevare un'eccezione solo per ispezionarla, ad esempio per scopi di registrazione. Dopo l'ispezione, si desidera che l'eccezione continui a propagarsi come prima.
In questo caso, usa semplicemente l'istruzione raise
senza parametri.
try:
5 / 0
except ZeroDivisionError:
print("Got an error")
raise
Tieni presente, tuttavia, che qualcuno più in alto nella pila dei chiamanti può ancora cogliere l'eccezione e gestirla in qualche modo. Il risultato finale potrebbe essere un fastidio in questo caso perché accadrà in ogni caso (catturato o non catturato). Quindi potrebbe essere un'idea migliore sollevare un'eccezione diversa, contenente il tuo commento sulla situazione e l'eccezione originale:
try:
5 / 0
except ZeroDivisionError as e:
raise ZeroDivisionError("Got an error", e)
Ma questo ha l'inconveniente di ridurre la traccia di eccezioni esattamente a questo raise
mentre il raise
senza argomento mantiene la traccia di eccezione originale.
In Python 3 puoi mantenere lo stack originale usando la sintassi raise
- from
:
raise ZeroDivisionError("Got an error") from e
Eccezioni a catena con rilancio da
Durante il processo di gestione di un'eccezione, potresti sollevare un'altra eccezione. Ad esempio, se si ottiene un IOError
durante la lettura da un file, è possibile che si desideri sollevare un errore specifico dell'applicazione da presentare agli utenti della libreria.
È possibile concatenare le eccezioni per mostrare come è stata gestita la gestione delle eccezioni:
>>> try:
5 / 0
except ZeroDivisionError as e:
raise ValueError("Division failed") from e
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
ZeroDivisionError: division by zero
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
ValueError: Division failed
Gerarchia delle eccezioni
La gestione delle eccezioni si verifica in base a una gerarchia di eccezioni, determinata dalla struttura di ereditarietà delle classi di eccezioni.
Ad esempio, IOError
e OSError
sono entrambe sottoclassi di EnvironmentError
. Il codice che cattura un IOError
non cattura un OSError
. Tuttavia, il codice che cattura un EnvironmentError
catturerà sia IOError
che OSError
.
La gerarchia delle eccezioni built-in:
BaseException
+-- SystemExit
+-- KeyboardInterrupt
+-- GeneratorExit
+-- Exception
+-- StopIteration
+-- StandardError
| +-- BufferError
| +-- ArithmeticError
| | +-- FloatingPointError
| | +-- OverflowError
| | +-- ZeroDivisionError
| +-- AssertionError
| +-- AttributeError
| +-- EnvironmentError
| | +-- IOError
| | +-- OSError
| | +-- WindowsError (Windows)
| | +-- VMSError (VMS)
| +-- EOFError
| +-- ImportError
| +-- LookupError
| | +-- IndexError
| | +-- KeyError
| +-- MemoryError
| +-- NameError
| | +-- UnboundLocalError
| +-- ReferenceError
| +-- RuntimeError
| | +-- NotImplementedError
| +-- SyntaxError
| | +-- IndentationError
| | +-- TabError
| +-- SystemError
| +-- TypeError
| +-- ValueError
| +-- UnicodeError
| +-- UnicodeDecodeError
| +-- UnicodeEncodeError
| +-- UnicodeTranslateError
+-- Warning
+-- DeprecationWarning
+-- PendingDeprecationWarning
+-- RuntimeWarning
+-- SyntaxWarning
+-- UserWarning
+-- FutureWarning
+-- ImportWarning
+-- UnicodeWarning
+-- BytesWarning
BaseException
+-- SystemExit
+-- KeyboardInterrupt
+-- GeneratorExit
+-- Exception
+-- StopIteration
+-- StopAsyncIteration
+-- ArithmeticError
| +-- FloatingPointError
| +-- OverflowError
| +-- ZeroDivisionError
+-- AssertionError
+-- AttributeError
+-- BufferError
+-- EOFError
+-- ImportError
+-- 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
+-- SyntaxError
| +-- IndentationError
| +-- TabError
+-- SystemError
+-- TypeError
+-- ValueError
| +-- UnicodeError
| +-- UnicodeDecodeError
| +-- UnicodeEncodeError
| +-- UnicodeTranslateError
+-- Warning
+-- DeprecationWarning
+-- PendingDeprecationWarning
+-- RuntimeWarning
+-- SyntaxWarning
+-- UserWarning
+-- FutureWarning
+-- ImportWarning
+-- UnicodeWarning
+-- BytesWarning
+-- ResourceWarning
Le eccezioni sono anche gli oggetti
Le eccezioni sono solo oggetti Python regolari che ereditano dalla BaseException
. Uno script Python può usare l'istruzione raise
per interrompere l'esecuzione, facendo in modo che Python stampi una traccia dello stack dello stack di chiamate in quel punto e una rappresentazione dell'istanza di eccezione. Per esempio:
>>> def failing_function():
... raise ValueError('Example error!')
>>> failing_function()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in failing_function
ValueError: Example error!
che dice che un 'Example error!'
ValueError
con il messaggio 'Example error!'
è stato sollevato dal nostro failing_function()
, che è stato eseguito nell'interprete.
Il codice chiamante può scegliere di gestire tutti i tipi di eccezioni che possono essere sollevate da una chiamata:
>>> try:
... failing_function()
... except ValueError:
... print('Handled the error')
Handled the error
È possibile ottenere gli oggetti eccezione assegnandoli nella parte except...
del codice di gestione delle eccezioni:
>>> try:
... failing_function()
... except ValueError as e:
... print('Caught exception', repr(e))
Caught exception ValueError('Example error!',)
Un elenco completo delle eccezioni Python integrate con le loro descrizioni può essere trovato nella documentazione di Python: https://docs.python.org/3.5/library/exceptions.html . Ed ecco l'elenco completo organizzato gerarchicamente: Gerarchia delle eccezioni .
Creazione di tipi di eccezioni personalizzate
Creare una classe che eredita da Exception
:
class FooException(Exception):
pass
try:
raise FooException("insert description here")
except FooException:
print("A FooException was raised.")
o un altro tipo di eccezione:
class NegativeError(ValueError):
pass
def foo(x):
# function that only accepts positive values of x
if x < 0:
raise NegativeError("Cannot process negative numbers")
... # rest of function body
try:
result = foo(int(input("Enter a positive integer: "))) # raw_input in Python 2.x
except NegativeError:
print("You entered a negative number!")
else:
print("The result was " + str(result))
Non prendere tutto!
Mentre è spesso allettante catturare ogni Exception
:
try:
very_difficult_function()
except Exception:
# log / try to reconnect / exit gratiously
finally:
print "The END"
# it runs no matter what execute.
O anche tutto (incluso BaseException
e tutti i suoi figli, inclusa l' Exception
):
try:
even_more_difficult_function()
except:
pass # do whatever needed
Nella maggior parte dei casi è una cattiva pratica. Potrebbe prendere più del previsto, come SystemExit
, KeyboardInterrupt
e MemoryError
, ognuno dei quali dovrebbe essere gestito in modo diverso dal solito sistema o errori logici. Significa anche che non c'è una chiara comprensione di ciò che il codice interno potrebbe fare male e come recuperare correttamente da quella condizione. Se stai rilevando ogni errore, non sai che errore si è verificato o come risolverlo.
Questo è più comunemente indicato come "bug masking" e dovrebbe essere evitato. Lascia che il tuo programma si blocchi, invece di fallire silenziosamente o, peggio ancora, fallire a un livello più profondo di esecuzione. (Immagina che sia un sistema transazionale)
Di solito questi costrutti sono usati al livello più esterno del programma e registreranno i dettagli dell'errore in modo che il bug possa essere corretto, o l'errore può essere gestito in modo più specifico.
Cattura più eccezioni
Ci sono alcuni modi per catturare più eccezioni .
Il primo è creando una tupla dei tipi di eccezioni che si desidera catturare e gestire allo stesso modo. Questo esempio farà sì che il codice ignori le eccezioni KeyError
e AttributeError
.
try:
d = {}
a = d[1]
b = d.non_existing_field
except (KeyError, AttributeError) as e:
print("A KeyError or an AttributeError exception has been caught.")
Se desideri gestire diverse eccezioni in modi diversi, puoi fornire un blocco di eccezioni separato per ogni tipo. In questo esempio, rileviamo ancora KeyError
e AttributeError
, ma gestiamo le eccezioni in modi diversi.
try:
d = {}
a = d[1]
b = d.non_existing_field
except KeyError as e:
print("A KeyError has occurred. Exception message:", e)
except AttributeError as e:
print("An AttributeError has occurred. Exception message:", e)
Esempi pratici di gestione delle eccezioni
Input dell'utente
Immagina di voler che un utente inserisca un numero tramite input
. Vuoi assicurarti che l'input sia un numero. Puoi usare try
/ except
per questo:
while True:
try:
nb = int(input('Enter a number: '))
break
except ValueError:
print('This is not a number, try again.')
Nota: Python 2.x raw_input
invece raw_input
; la funzione input
esiste in Python 2.x ma ha semantica diversa. Nell'esempio sopra, l' input
accetta anche espressioni come 2 + 2
che valutano un numero.
Se l'input non può essere convertito in un numero intero, viene sollevata ValueError
. Puoi prenderlo con l' except
. Se non viene sollevata alcuna eccezione, break
salti fuori dal ciclo. Dopo il ciclo, nb
contiene un numero intero.
dizionari
Immaginate effettua l'iterazione di un elenco di numeri interi consecutivi, come range(n)
, e si dispone di un elenco di dizionari d
che contiene le informazioni sulle cose da fare quando si incontrano alcuni interi particolari, dici saltare il d[i]
i prossimi.
d = [{7: 3}, {25: 9}, {38: 5}]
for i in range(len(d)):
do_stuff(i)
try:
dic = d[i]
i += dic[i]
except KeyError:
i += 1
Un KeyError
verrà generato quando si tenta di ottenere un valore da un dizionario per una chiave che non esiste.
Altro
Il codice in un altro blocco verrà eseguito solo se non sono state sollevate eccezioni dal codice nel blocco try
. Questo è utile se hai un codice che non vuoi eseguire se viene lanciata un'eccezione, ma non vuoi che vengano catturate le eccezioni generate da quel codice.
Per esempio:
try:
data = {1: 'one', 2: 'two'}
print(data[1])
except KeyError as e:
print('key not found')
else:
raise ValueError()
# Output: one
# Output: ValueError
Nota che questo tipo di else:
non può essere combinato con un if
avvia la clausola else in un elif
. Se si dispone di un seguito if
ha bisogno per rimanere rientrato sotto che else:
:
try:
...
except ...:
...
else:
if ...:
...
elif ...:
...
else:
...