exception custom - Aumentare manualmente(lanciare)un'eccezione in Python




raise try (5)

Come posso aumentare un'eccezione in Python in modo che possa essere catturata in un secondo momento tramite un blocco except ?


Answers

Per il caso comune in cui è necessario lanciare un'eccezione in risposta ad alcune condizioni impreviste e che non si intende catturare, ma semplicemente fallire velocemente per consentire di eseguire il debug da lì se dovesse mai 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)

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

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

Esempio:

raise SystemExit

esce dal programma ma potresti voler sapere cosa è successo. Puoi usare questo.

raise SystemExit("program exited")

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


In Python3 ci sono 4 diverse sintassi per le eccezioni di 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 l' raise exception (args) per generare un'eccezione, gli args verranno stampati quando si stampa l'oggetto di 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

raise dichiarazione senza alcun argomento ridimensiona l'ultima eccezione. Questo è utile se hai bisogno di eseguire alcune azioni dopo aver catturato l'eccezione e poi vuoi rilanciarlo. Ma se non ci sono state eccezioni prima, raise dichiarazione di raise solleva l'eccezione TypeError .

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 concatenazioni di eccezioni in cui un'eccezione che viene generata 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

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

Utilizzare il costruttore di eccezioni 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 generare un'eccezione generica. Per catturarlo, dovrai catturare tutte le altre eccezioni più specifiche che la sottoclassi.

Problema 1: nascondendo 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 colgono 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: dichiarazione di merito

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

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

che consente anche facilmente un numero arbitrario di argomenti da passare al costruttore:

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

Questi argomenti sono accessibili dall'attributo args sull'oggetto Exception. 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, un attributo di message effettivo è stato aggiunto a BaseException in favore di incoraggiare gli utenti a eseguire la sottoclasse di eccezioni e interrompere l'uso di args , ma l'introduzione del message e la deprecazione originale di args sono state ritirate .

Best practice: except clausola

Quando si trova all'interno di una clausola except, è possibile, ad esempio, registrare che si è verificato un tipo specifico di errore e quindi controrilanciare. Il modo migliore per farlo mentre si preserva la traccia dello stack è usare una semplice istruzione raise. 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 preservare lo stacktrace (e il valore di errore) con sys.exc_info() , ma questo è molto più incline agli errori e ha problemi di compatibilità tra Python 2 e 3 , preferisci usare un raise per controrilanciare.

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 questo 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 nuovi argomenti per 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 conservato l'intero traceback modificando gli argomenti. Nota che questa non è una best practice ed è una sintassi non valida in Python 3 (rendendo la compatibilità molto più difficile da aggirare).

>>> 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: evitare di manipolare manualmente i traceback. È meno efficiente e più soggetto a errori. E se utilizzi il threading e sys.exc_info potresti persino ottenere il traceback sbagliato (specialmente se stai utilizzando la gestione delle eccezioni per il controllo del flusso, che personalmente tenderei ad evitare).

Python 3, concatenamento di eccezioni

In Python 3, puoi concatenare Eccezioni, che preservano i traceback:

    raise RuntimeError('specific message') from error

Essere consapevoli:

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

Metodi sconsigliati:

Questi possono facilmente nascondere e persino entrare nel codice di produzione. Vuoi sollevare un'eccezione e farle 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 in versioni molto vecchie di Python (2.4 e precedenti), puoi ancora vedere persone che sollevano le stringhe:

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

In tutte le versioni moderne, questo genererà effettivamente un errore TypeError, perché non stai generando un tipo BaseException. Se non stai cercando l'eccezione giusta e non hai un revisore consapevole del problema, potrebbe entrare in produzione.

Esempio di utilizzo

Alzo le eccezioni per avvisare i consumatori della mia API se la utilizzano 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 opportuno

"Voglio fare un errore di proposito, in modo che andasse nell'eccezione"

Puoi creare i tuoi tipi di errore, se vuoi indicare qualcosa di specifico è sbagliato nella tua applicazione, basta sottoclassi 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.')

Questo è un classico problema che ogni sviluppatore C # affronta alla fine.

Permettetemi di rompere la tua domanda in 2 domande. Il primo,

Posso rilevare più eccezioni contemporaneamente?

In breve, no.

Che porta alla prossima domanda,

Come evitare di scrivere codice duplicato dato che non riesco a catturare più tipi di eccezione nello stesso blocco catch ()?

Dato il tuo campione specifico, in cui il valore di fallback è economico da costruire, mi piace seguire questi passaggi:

  1. Inizializza WebId al valore di fallback.
  2. Costruisci una nuova guida in una variabile temporanea.
  3. Imposta WebId sulla variabile temporanea completamente costruita. Rendi questa la dichiarazione finale del blocco try {}.

Quindi il codice sembra:

try
{
    WebId = Guid.Empty;
    Guid newGuid = new Guid(queryString["web"]);
    // More initialization code goes here like 
    // newGuid.x = y;
    WebId = newGuid;
}
catch (FormatException) {}
catch (OverflowException) {}

Se viene generata un'eccezione, WebId non viene mai impostato sul valore semistrutturato e rimane Guid.Empty.

Se la costruzione del valore di fallback è costosa, e il ripristino di un valore è molto più economico, allora sposterei il codice di reset nella sua funzione:

try
{
    WebId = new Guid(queryString["web"]);
    // More initialization code goes here.
}
catch (FormatException) {
    Reset(WebId);
}
catch (OverflowException) {
    Reset(WebId);
}




python exception exception-handling