Sollevare (lanciare) manualmente un'eccezione in Python


2266

Come posso sollevare un'eccezione in Python in modo che possa essere successivamente catturato tramite un exceptblocco?

Risposte:


2939

Come faccio a lanciare / sollevare manualmente un'eccezione in Python?

Utilizzare il costruttore Eccezione più specifico che si adatta semanticamente al problema .

Sii specifico nel tuo messaggio, ad esempio:

raise ValueError('A very specific bad thing happened.')

Non sollevare eccezioni generiche

Evita di sollevare un generico Exception . Per catturarlo, dovrai catturare tutte le altre eccezioni più specifiche che lo sottoclassano.

Problema 1: nascondere i bug

raise Exception('I know Python!') # Don't! If you catch, likely to hide bugs.

Per esempio:

def demo_bad_catch():
    try:
        raise ValueError('Represents a hidden bug, do not catch this')
        raise Exception('This is the exception you expect to handle')
    except Exception as error:
        print('Caught this error: ' + repr(error))

>>> demo_bad_catch()
Caught this error: ValueError('Represents a hidden bug, do not catch this',)

Problema 2: non prenderà

E le catture più specifiche non colpiranno l'eccezione generale:

def demo_no_catch():
    try:
        raise Exception('general exceptions not caught by specific handling')
    except ValueError as e:
        print('we will not catch exception: Exception')


>>> demo_no_catch()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in demo_no_catch
Exception: general exceptions not caught by specific handling

Best practice: raisedichiarazione

Utilizzare invece il costruttore Eccezione più specifico che si adatta semanticamente al problema .

raise ValueError('A very specific bad thing happened')

che consente anche di trasmettere un numero arbitrario di argomenti al costruttore:

raise ValueError('A very specific bad thing happened', 'foo', 'bar', 'baz') 

Questi argomenti sono accessibili dal argsattributo sul Exceptionoggetto. Per esempio:

try:
    some_code_that_may_raise_our_value_error()
except ValueError as err:
    print(err.args)

stampe

('message', 'foo', 'bar', 'baz')    

In Python 2.5 è messagestato aggiunto un attributo effettivo BaseExceptiona favore di incoraggiare gli utenti a sottoclassare Eccezioni e smettere di usare args, ma l'introduzione messagee la deprecazione originale di args sono state ritirate .

Best practice: exceptclausola

Quando si è all'interno di una clausola exception, è possibile, ad esempio, registrare che si sia verificato un tipo specifico di errore e quindi ripetere il rilancio. Il modo migliore per farlo preservando la traccia dello stack è usare un'istruzione di rilancio nudo. Per esempio:

logger = logging.getLogger(__name__)

try:
    do_something_in_app_that_breaks_easily()
except AppError as error:
    logger.error(error)
    raise                 # just this!
    # raise AppError      # Don't do this, you'll lose the stack trace!

Non modificare i tuoi errori ... ma se insisti.

Puoi conservare lo stacktrace (e il valore dell'errore) con sys.exc_info(), ma questo è molto più soggetto a errori e ha problemi di compatibilità tra Python 2 e 3 , preferisci usare un bare raiseper rilanciare.

Per spiegare: sys.exc_info()restituisce il tipo, il valore e il traceback.

type, value, traceback = sys.exc_info()

Questa è la sintassi in Python 2 - nota che non è compatibile con Python 3:

    raise AppError, error, sys.exc_info()[2] # avoid this.
    # Equivalently, as error *is* the second object:
    raise sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]

Se lo desideri, puoi modificare ciò che accade con il tuo nuovo rilancio, ad esempio impostando nuovo argsper l'istanza:

def error():
    raise ValueError('oops!')

def catch_error_modify_message():
    try:
        error()
    except ValueError:
        error_type, error_instance, traceback = sys.exc_info()
        error_instance.args = (error_instance.args[0] + ' <modification>',)
        raise error_type, error_instance, traceback

E abbiamo preservato l'intero traceback durante la modifica degli arg. Nota che questa non è una buona pratica ed è una sintassi non valida in Python 3 (rendendo molto più difficile mantenere la compatibilità).

>>> catch_error_modify_message()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in catch_error_modify_message
  File "<stdin>", line 2, in error
ValueError: oops! <modification>

In Python 3 :

    raise error.with_traceback(sys.exc_info()[2])

Ancora: evita di manipolare manualmente i traceback. È meno efficiente e più soggetto a errori. E se stai usando il threading e sys.exc_infopotresti persino avere il traceback sbagliato (specialmente se stai usando la gestione delle eccezioni per il flusso di controllo - che personalmente tenderei ad evitare).

Python 3, concatenamento delle eccezioni

In Python 3, è possibile concatenare le eccezioni, che conservano i traceback:

    raise RuntimeError('specific message') from error

Attenzione:

  • questo non permette di cambiare il tipo di errore sollevato, e
  • questo non è compatibile con Python 2.

Metodi obsoleti:

Questi possono facilmente nascondere e persino entrare nel codice di produzione. Vuoi sollevare un'eccezione, e farli genererà un'eccezione, ma non quella prevista!

Valido in Python 2, ma non in Python 3 è il seguente:

raise ValueError, 'message' # Don't do this, it's deprecated!

Valido solo nelle versioni molto più vecchie di Python (2.4 e precedenti), potresti ancora vedere persone che sollevano stringhe:

raise 'message' # really really wrong. don't do this.

In tutte le versioni moderne, questo aumenterà effettivamente a TypeError, perché non stai aumentando un BaseExceptiontipo. Se non stai verificando la giusta eccezione e non hai un revisore a conoscenza del problema, potrebbe entrare in produzione.

Esempio di utilizzo

Sollevo eccezioni per avvertire i consumatori della mia API se la usano in modo errato:

def api_func(foo):
    '''foo should be either 'baz' or 'bar'. returns something very useful.'''
    if foo not in _ALLOWED_ARGS:
        raise ValueError('{foo} wrong, use "baz" or "bar"'.format(foo=repr(foo)))

Crea i tuoi tipi di errore quando apropos

"Voglio fare un errore di proposito, in modo che andrebbe in tranne"

È possibile creare i propri tipi di errore, se si desidera indicare che qualcosa di specifico non va nella propria applicazione, è sufficiente sottoclassare il punto appropriato nella gerarchia delle eccezioni:

class MyAppLookupError(LookupError):
    '''raise this when there's a lookup error for my app'''

e utilizzo:

if important_key not in resource_dict and not ok_to_be_missing:
    raise MyAppLookupError('resource is missing, and that is not ok.')

19
Grazie per questo, è esattamente quello di cui avevo bisogno. Il nudo raiseè ciò di cui avevo bisogno per poter eseguire il debug degli errori personalizzato a più livelli di esecuzione del codice senza interrompere la traccia dello stack.
CaffeineConnoisseur

Questa è un'ottima risposta Ma lavoro ancora con un sacco di codice 2.7 e spesso mi ritrovo a voler aggiungere informazioni a un'eccezione imprevista, come una posizione del file di input o i valori di alcune variabili, ma mantengo lo stack e l'eccezione originali. Posso registrarlo, ma a volte non lo voglio registrato, ad esempio se il codice genitore alla fine lo gestisce. raise sys.exc_info()[0], (sys.exc_info()[1], my_extra_info), sys.exc_info()[2]sembra fare quello che voglio e non ho mai avuto problemi con esso. Ma sembra confuso, e non una pratica accettata. Esiste un modo migliore?
Michael Scheper,

2
@brennanyoung In quel contesto penso che potrebbe essere fonte di confusione sollevare un SyntaxError - probabilmente dovresti sollevare un'eccezione personalizzata. Spiego come: stackoverflow.com/a/26938914/541136
Aaron Hall

2
Si noti che la citazione completa è "Tutte le eccezioni integrate, non uscenti dal sistema, derivano da questa classe. Anche tutte le eccezioni definite dall'utente devono essere derivate da questa classe." - Ciò significa principalmente che non dovresti usare una delle 4 eccezioni che non derivano dalla Exceptiontua classe genitore - puoi sottoclassare qualcosa di più specifico e dovresti farlo se ha senso.
Aaron Hall

1
Nell'esempio per " Best Practices: tranne la clausola ", si utilizza AppErrorun'eccezione non definita . Potrebbe essere meglio usare un errore incorporato comeAttributeError
Stevoisiak

530

NON FARE QUESTO . Crescere un nuda Exceptionè assolutamente non la cosa giusta da fare; vedi invece l'eccellente risposta di Aaron Hall .

Non posso ottenere molto più pitonico di così:

raise Exception("I know python!")

Vedi i documenti dell'istruzione raise per Python se desideri maggiori informazioni.


67
No per favore! Questo rimuove il potenziale per essere specifico su ciò che catturi. È INTERAMENTE il modo sbagliato di farlo. Dai un'occhiata all'ottima risposta di Aaron Hall invece di questa. In momenti come questo vorrei poter dare più di un voto negativo per risposta.
Dawood ibn Kareem,

27
@PeterR È altrettanto terribile che abbia così pochi voti negativi. A QUALUNQUE lettura di questa risposta, NON FARLO MAI! La risposta corretta è quella di Aaron Hall.
Dawood ibn Kareem,

6
Penso che ci dovrebbe essere una spiegazione più dettagliata sul perché questo è sbagliato o così cattivo.
Charlie Parker,

9
@CharlieParker C'è. È la prima parte della risposta di Aaron Hall .
Dinei,

5
Perché questa risposta non può essere contrassegnata per l'eliminazione? Ha già 93 voti negativi!
codeforester,

55

In Python3 ci sono 4 diverse sintassi per le eccezioni rasing:

1. raise exception 
2. raise exception (args) 
3. raise
4. raise exception (args) from original_exception

1. aumentare l'eccezione rispetto a 2. aumentare l'eccezione (args)

Se si utilizza raise exception (args) per sollevare un'eccezione, argsverrà stampato quando si stampa l'oggetto dell'eccezione, come mostrato nell'esempio seguente.

  #raise exception (args)
    try:
        raise ValueError("I have raised an Exception")
    except ValueError as exp:
        print ("Error", exp)     # Output -> Error I have raised an Exception 



  #raise execption 
    try:
        raise ValueError
    except ValueError as exp:
        print ("Error", exp)     # Output -> Error 

3.raise

raiseL'istruzione senza argomenti ripristina l'ultima eccezione. Ciò è utile se è necessario eseguire alcune azioni dopo aver rilevato l'eccezione e quindi si desidera aumentare nuovamente. Ma se prima non c'erano eccezioni, l' raiseaffermazione genera l' TypeErroreccezione.

def somefunction():
    print("some cleaning")

a=10
b=0 
result=None

try:
    result=a/b
    print(result)

except Exception:            #Output ->
    somefunction()           #some cleaning
    raise                    #Traceback (most recent call last):
                             #File "python", line 8, in <module>
                             #ZeroDivisionError: division by zero

4. solleva l'eccezione (args) da original_exception

Questa istruzione viene utilizzata per creare un concatenamento di eccezioni in cui un'eccezione sollevata in risposta a un'altra eccezione può contenere i dettagli dell'eccezione originale, come mostrato nell'esempio seguente.

class MyCustomException(Exception):
pass

a=10
b=0 
reuslt=None
try:
    try:
        result=a/b

    except ZeroDivisionError as exp:
        print("ZeroDivisionError -- ",exp)
        raise MyCustomException("Zero Division ") from exp

except MyCustomException as exp:
        print("MyException",exp)
        print(exp.__cause__)

Produzione:

ZeroDivisionError --  division by zero
MyException Zero Division 
division by zero

7
Si prega di notare che PEP8 preferisce exception(args)overexception (args)
Gloweye

C'è anche raise exception(args) from Noneda dire che l'eccezione attualmente attiva è stata gestita e non è più interessante. Altrimenti se si genera un'eccezione all'interno di un exceptblocco e non viene gestita, i traceback per entrambe le eccezioni verranno visualizzati separati dal messaggio "Durante la gestione dell'eccezione sopra, si è verificata un'altra eccezione"
cg909

35

Per il caso comune in cui è necessario lanciare un'eccezione in risposta ad alcune condizioni impreviste e che non si intende mai catturare, ma semplicemente fallire rapidamente per consentire di eseguire il debug da lì, se mai dovesse accadere, il più logico sembra essere AssertionError:

if 0 < distance <= RADIUS:
    #Do something.
elif RADIUS < distance:
    #Do something.
else:
    raise AssertionError("Unexpected value of 'distance'!", distance)

19
Questo è un caso migliore di ValueErrorquello AssertionErrorperché non ci sono problemi con un'asserzione (perché qui non ne viene fatta nessuna) - il problema è con un valore. Se vuoi davvero un AssertionErrorin questo caso, scrivi assert distance > 0, 'Distance must be positive'. Ma non dovresti controllare l'errore in questo modo perché le asserzioni possono essere disattivate ( python -O).
Two-Bit Alchemist,

1
@ Two-BitAlchemist Un buon punto. L'idea è stata persa nella semplificazione, quando ho scritto il semplice esempio sopra. In molti casi simili è una condizione che non è associata a un valore particolare. Piuttosto, il significato è "il flusso di controllo non dovrebbe mai arrivare qui".
Evgeni Sergeev,

2
@ Le asserzioni Two-BitAlchemist possono essere disattivate, sì, ma non dovresti usarle per controllare l'errore?
Evgeni Sergeev,

Beh, dipende. Non lascerei che questo fosse il mio unico errore durante il controllo in un programma che intendevo distribuire. D'altra parte, potrei fare un programma solo per i miei colleghi e dire loro che lo usano a loro rischio e pericolo se lo eseguono -O.
Two-Bit Alchemist,

1
@ Two-BitAlchemist Per me il ruolo delle asserzioni non è il controllo degli errori di per sé (che è ciò a cui serve il test), ma hanno creato recinzioni all'interno del codice che alcuni bug non riescono a superare. Quindi diventa più facile rintracciare e isolare i bug, che inevitabilmente si verificheranno. Queste sono solo buone abitudini che richiedono poco sforzo, mentre i test richiedono molto sforzo e molto tempo.
Evgeni Sergeev,

12

Leggi prima le risposte esistenti, questo è solo un addendum.

Si noti che è possibile sollevare eccezioni con o senza argomenti.

Esempio:

raise SystemExit

esce dal programma ma potresti voler sapere cosa è successo, quindi puoi usarlo.

raise SystemExit("program exited")

questo stamperà "programma uscito" su stderr prima di chiudere il programma.


2
Non è questo contro il paradigma OOP? Presumo, il primo caso genera il riferimento di classe e il secondo un'istanza di SystemExit. Non raise SystemExit()sarebbe la scelta migliore? Perché il primo funziona anche?
Bruciante

2

Un altro modo per generare un'eccezione è assert. È possibile utilizzare assert per verificare che una condizione sia soddisfatta, altrimenti si solleverà AssertionError. Per maggiori dettagli dai un'occhiata qui .

def avg(marks):
    assert len(marks) != 0,"List is empty."
    return sum(marks)/len(marks)

mark2 = [55,88,78,90,79]
print("Average of mark2:",avg(mark2))

mark1 = []
print("Average of mark1:",avg(mark1))

2

Solo per notare: ci sono momenti in cui vuoi gestire eccezioni generiche. Se stai elaborando un mucchio di file e stai registrando i tuoi errori, potresti voler rilevare qualsiasi errore che si verifica per un file, registrarlo e continuare a elaborare il resto dei file. In tal caso, a

try:
    foo() 
except Exception as e:
    print(str(e)) # Print out handled error

bloccare un buon modo per farlo. Avrai comunque bisogno di raiseeccezioni specifiche in modo da sapere cosa significano, però.


0

Per questo dovresti imparare la dichiarazione di rilancio di Python. Dovrebbe essere tenuto all'interno del blocco try. Esempio -

try:
    raise TypeError            #remove TypeError by any other error if you want
except TypeError:
    print('TypeError raised')
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.