la cláusula except borra la variable local

exc = None try: raise Exception except Exception as exc: pass # ... print(exc) 

NameError: el nombre ‘exc’ no está definido

Esto solía trabajar en Python2. ¿Por qué se cambió de esta manera? Si pudiera al menos reasignar a exc , similar a los atributos de nivel de clase

 class Foo(object): Bar = Bar 

Pero esto tampoco lo hace funcionar:

 exc = None try: raise Exception except Exception as exc: exc = exc 

¿Alguna buena pista para lograr lo mismo? Prefiero no escribir algo como esto:

 exc = None try: raise Exception("foo") except Exception as e: exc = e # ... print(exc) 

La statement de prueba limita explícitamente el scope de la excepción enlazada, para evitar que las referencias circulares provoquen fugas. Vea la documentación de la statement de try :

Cuando se ha asignado una excepción utilizando como destino, se borra al final de la cláusula de excepción .

[…]

Esto significa que la excepción debe asignarse a un nombre diferente para poder referirse a ella después de la cláusula de excepción . Las excepciones se eliminan porque con el rastreo adjunto a ellas, forman un ciclo de referencia con el marco de la stack, manteniendo con vida a todos los locales en ese marco hasta que se produzca la próxima recolección de basura.

Énfasis mío; tenga en cuenta que su única opción es vincular la excepción a un nuevo nombre.

En Python 2, las excepciones no tenían una referencia al rastreo, por lo que se cambió esto.

Sin embargo, incluso en Python 2, se le advierte explícitamente sobre la limpieza de las trazas, consulte sys.exc_info() :

Advertencia : la asignación del valor de retorno de rastreo a una variable local en una función que maneja una excepción causará una referencia circular. Esto evitará que cualquier cosa referenciada por una variable local en la misma función o por el rastreo sea recolectada como basura. Como la mayoría de las funciones no necesitan acceso al rastreo, la mejor solución es usar algo como exctype, value = sys.exc_info()[:2] para extraer solo el tipo y el valor de la excepción. Si necesita el rastreo, asegúrese de eliminarlo después de usarlo (lo mejor es hacerlo con una statement try ... finally ) o llamar a exc_info() en una función que no maneja una excepción.

Si vuelve a enlazar la excepción, es posible que desee borrar el rastreo explícitamente:

 try: raise Exception("foo") except Exception as e: exc = e exc.__traceback__ = None