Qu'arrive-t-il à ma première exception (A
) lorsque la seconde (B
) est déclenchée dans le code suivant?
class A(Exception): pass
class B(Exception): pass
try:
try:
raise A('first')
finally:
raise B('second')
except X as c:
print(c)
Si exécuté avec X = A
Je reçois:
Traceback (dernier appel le plus récent): Fichier "raise_more_exceptions.py", ligne 6, dans Augmenter A ('premier') __ main __. A: premier Lors du traitement de l'exception ci-dessus, une autre exception s'est produite: Traceback (dernier appel en date): Fichier "raise_more_exceptions.py", ligne 8, dans augmenter B ('deuxième') __ principal __. B: deuxième
Mais si X = B
Je reçois:
seconde
Cette question concerne spécifiquement Python 3, car sa gestion des exceptions est assez différente de Python 2.
En réponse à la question 3, vous pouvez utiliser:
raise B('second') from None
Ce qui supprimera l'exception A
traceback.
Traceback (most recent call last):
File "raising_more_exceptions.py", line 8, in
raise B('second')
__main__.B: second
L'exception "causante" est disponible sous la forme c .__ context__ dans votre dernier gestionnaire d'exceptions. Python utilise ces informations pour rendre un suivi plus utile. Sous Python 2.x, l'exception d'origine aurait été perdue, c'est pour Python 3 uniquement.
En règle générale, vous utiliseriez cela pour lever une exception cohérente tout en conservant l'exception d'origine accessible (bien que ce soit assez cool que cela se produise automatiquement à partir d'un gestionnaire d'exceptions, je ne le savais pas!):
try:
do_something_involving_http()
except (URLError, socket.timeout) as ex:
raise MyError('Network error') from ex
Plus d'informations (et d'autres choses assez utiles que vous pouvez faire) ici: http://docs.python.org/3.3/library/exceptions.html
La gestion des exceptions Pythons ne traitera qu'une seule exception à la fois. Cependant, les objets d'exception sont soumis aux mêmes règles de variables et au même garbage collection que tout le reste. Par conséquent, si vous enregistrez l'objet exception dans une variable quelque part, vous pouvez le traiter plus tard, même si une autre exception est déclenchée.
Dans votre cas, lorsqu'une exception est levée lors de l'instruction "finally", Python 3 affichera le traceback de la première exception avant celle de la deuxième exception, pour être plus utile.
Un cas plus courant est que vous souhaitez déclencher une exception lors d'un traitement d'exception explicite. Ensuite, vous pouvez "enregistrer" l'exception dans l'exception suivante. Passez-le simplement comme paramètre:
>>> class A(Exception):
... pass
...
>>> class B(Exception):
... pass
...
>>> try:
... try:
... raise A('first')
... except A as e:
... raise B('second', e)
... except Exception as c:
... print(c.args[1])
...
first
Comme vous le voyez, vous pouvez désormais accéder à l'exception d'origine.
Je pense que tous les ingrédients pour répondre à vos questions figurent déjà dans les réponses existantes. Permettez-moi de combiner et d'élaborer.
Permettez-moi de répéter le code de votre question pour fournir des références de numéro de ligne:
1 class A(Exception): pass
2 class B(Exception): pass
3
4 try:
5 try:
6 raise A('first')
7 finally:
8 raise B('second')
9 except X as c:
10 print(c)
Alors pour répondre à vos questions:
Votre première exception A
est levée à la ligne 6. La clause finally
à la ligne 7 est toujours exécutée dès que le bloc try
(lignes 5-6) est laissé, qu'il soit laissé en raison d'une réussite ou d'une exception levée. Pendant l'exécution de la clause finally
, la ligne 8 lève une autre exception B
. Comme l'ont souligné Lennart et Ignazio, une seule exception, celle qui a été soulevée le plus récemment, peut être suivie. Ainsi, dès que B
est levé, le bloc global try
(lignes 4-8) est quitté et l'exception B
est interceptée par le except
à la ligne 9 si elle correspond (si X
est B
).
J'espère que cela est clair maintenant à partir de mon explication de 1. Vous pouvez cependant attraper l'exception intérieure/inférieure/première. Pour fusionner dans la réponse de Lennart, légèrement modifiée, voici comment intercepter les deux:
class A(Exception): pass
class B(Exception): pass
try:
try:
raise A('first')
except A as e:
raise B('second', e)
except Exception as c:
print(c)
La sortie est:
('second', A('first',))
Dans l'exemple de Lennart, la solution à cette question est la ligne except A as e
où l'exception interne/inférieure/première est interceptée et stockée dans la variable e
.
Comme un sentiment général de savoir quand attraper des exceptions, quand les ignorer et quand sur-relancer, peut-être cette question et la réponse d'Alex Martelli aide.