web-dev-qa-db-fra.com

"sauf Foo as bar" provoque la suppression de "bar" de l'oscilloscope

É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
42
knipknap

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 si

except 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.

51
Uku Loskit

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
33
blhsing

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

7
Michael