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.