Utilizzo "Python" da "


197

Qual è la differenza tra raisee raise fromin Python?

try:
    raise ValueError
except Exception as e:
    raise IndexError

che cede

Traceback (most recent call last):
  File "tmp.py", line 2, in <module>
    raise ValueError
ValueError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "tmp.py", line 4, in <module>
    raise IndexError
IndexError

e

try:
    raise ValueError
except Exception as e:
    raise IndexError from e

che cede

Traceback (most recent call last):
  File "tmp.py", line 2, in <module>
    raise ValueError
ValueError

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "tmp.py", line 4, in <module>
    raise IndexError from e
IndexError

9
Hai letto PEP-3134 ?
jonrsharpe,

4
Ora usa raise IndexError from None, diciamo.
Martijn Pieters

11
Eh. raise IndexError from Falsegenera un TypeError, non un IndexError. Ha reso la mia giornata.
Fisico pazzo,


Non sono sicuro se questo è il posto giusto per menzionarlo, ma per chiunque usi Spyder: l'intero costrutto non funziona lì. È stato un problema per oltre 3 anni ( github.com/spyder-ide/spyder/issues/2943 ), ma sembrano pensare che non siano necessarie eccezioni concatenate.
Emil Bode,

Risposte:


230

La differenza è che quando si utilizza froml' __cause__attributo viene impostato e il messaggio indica che l'eccezione è stata causata direttamente da . Se si omette, fromallora __cause__viene impostato no , ma è possibile impostare anche l' __context__attributo e il traceback mostra quindi il contesto come durante la gestione di qualcos'altro .

L'impostazione __context__succede se è stato utilizzato raisein un gestore eccezioni; se hai usato raisealtrove, non __context__è impostato neanche.

Se __cause__viene impostato a, __suppress_context__ = Trueviene impostato anche un flag sull'eccezione; quando __suppress_context__è impostato su True, __context__viene ignorato quando si stampa un traceback.

Quando si solleva da un gestore di eccezioni in cui non si desidera mostrare il contesto (non si desidera durante la gestione di un altro messaggio si è verificata un'eccezione ), quindi utilizzare raise ... from Noneper impostare __suppress_context__su True.

In altre parole, Python imposta un contesto sulle eccezioni in modo da poter introspettare dove è stata sollevata un'eccezione, consentendo di vedere se un'altra eccezione è stata sostituita da essa. È inoltre possibile aggiungere una causa a un'eccezione, rendendo esplicito il traceback sull'altra eccezione (utilizzare una diversa formulazione) e il contesto viene ignorato (ma può comunque essere introspetto durante il debug). L'uso raise ... from Noneconsente di sopprimere il contesto in fase di stampa.

Vedi la raisedocumentazione dichiarazione :

La fromclausola viene utilizzata per il concatenamento delle eccezioni: se fornita, la seconda espressione deve essere un'altra classe o istanza di eccezione, che verrà quindi allegata all'eccezione sollevata come __cause__attributo (che è scrivibile). Se l'eccezione sollevata non viene gestita, verranno stampate entrambe le eccezioni:

>>> try:
...     print(1 / 0)
... except Exception as exc:
...     raise RuntimeError("Something bad happened") from exc
...
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: int division or modulo by zero

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
RuntimeError: Something bad happened

Un meccanismo simile funziona implicitamente se viene sollevata un'eccezione all'interno di un gestore di eccezioni o di una finallyclausola: l'eccezione precedente viene quindi allegata come __context__attributo della nuova eccezione :

>>> try:
...     print(1 / 0)
... except:
...     raise RuntimeError("Something bad happened")
...
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: int division or modulo by zero

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
RuntimeError: Something bad happened

Vedere anche la documentazione Eccezioni incorporate per dettagli sul contesto e causare informazioni allegate alle eccezioni.


11
C'è qualche motivo per concatenare esplicitamente le eccezioni usando frome __cause__al posto dell'implicito __context__? Ci sono casi in cui si potrebbe allegare un'eccezione diversa da quella rilevata dal except?
darkfeline,

13
@darkfeline: supponiamo che l'API del database supporti l'apertura di database da varie fonti, inclusi Web e disco. L'API genererà sempre a DatabaseErrorse l'apertura del database non riesce. Ma se l'errore è il risultato di IOErrorperché un file non è stato aperto o HTTPErrorperché un URL non ha funzionato, allora è il contesto che si desidera includere esplicitamente, quindi lo sviluppatore che utilizza l'API può eseguire il debug del perché. In quel momento usi raise DatabaseError from original_exception.
Martijn Pieters

4
@darkfeline: se quello sviluppatore sta avvolgendo l'uso dell'API del database nella propria API e volesse trasmetterlo IOErroro HTTPErrorai propri consumatori, allora dovrebbero usare raise NewException from databaseexception.__cause__, ora usando un'eccezione diversa da quella DatabaseExceptionche hanno appena catturato.
Martijn Pieters

2
@ dan3: no, non c'è. Il concatenamento delle eccezioni è puramente una funzionalità di Python 3.
Martijn Pieters

5
@ laike9m: intendi quando gestisci l'eccezione fooe vuoi sollevare una nuova eccezione bar? Quindi è possibile utilizzare raise bar from fooe avere lo stato Python che ha foo causato direttamentebar . Se non lo usi from foo, Python stampa comunque entrambi, ma afferma che durante la gestione foo, è barstato generato un messaggio diverso, destinato a contrassegnare un possibile bug nella gestione degli errori.
Martijn Pieters
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.