Étant donné le code suivant:
msg = "test"
try:
"a"[1]
except IndexError as msg:
print("Error happened")
print(msg)
Quelqu'un peut-il expliquer pourquoi cela provoque la sortie suivante dans Python 3?
Error happened
Traceback (most recent call last):
File "test.py", line 6, in <module>
print(msg)
NameError: name 'msg' is not defined
msg
dans la clause except est dans la même portée que msg
sur la première ligne.
Mais in Python 3 nous avons aussi ce nouveau comportement :
Lorsqu'une exception a été affectée avec
as target
, il est effacé à la fin de la clause except. C'est comme siexcept E as N: foo
a été traduit en
except E as N: try: foo finally: del N
Cela signifie que l’exception doit être affectée à un nom différent pour pouvoir y faire référence après la clause except. Les exceptions sont effacées car, avec le suivi associé, elles forment un cycle de référence avec le cadre de pile, laissant toutes les données locales de ce cadre en vie jusqu'à la prochaine récupération de place.
donc, vous écrasez msg
dans le gestionnaire d'exceptions, et quitter le gestionnaire supprime la variable pour effacer le cycle de référence de trace.
Oui, dès que l'exception est déclenchée et que msg
est affecté au nouvel objet exception, l'objet d'origine n'a plus de référence et est donc supprimé. Le nouvel objet exception est également supprimé dès qu’il quitte le bloc except
.
Vous pouvez le vérifier en remplaçant le __del__
méthode de l'objet et exception affectée à msg
:
class A:
def __del__(self):
print('object deleted')
class E(Exception):
def __del__(self):
print('exception deleted')
msg = A()
try:
raise E()
except E as msg:
print("Error happened")
Cela génère:
object deleted
Error happened
exception deleted
NameError: name 'msg' is not defined
Les blocs d'exception suppriment la variable interceptée à la fin du bloc, mais ils ne possèdent pas leurs propres étendues. Alors la séquence des événements va:
1) msg
est défini sur une chaîne dans la portée locale
2) msg
est défini sur un objet IndexError dans la même portée locale que 1
3) msg
est supprimé de la portée locale à la fin du bloc d'exception
4) msg
n'est plus défini dans la portée locale, la tentative d'accès y échoue