La advertencia de error de tipo de excepción a veces se muestra, a veces no cuando se utiliza el método de generación de tiro

Hay este código:

class MyException(Exception): pass def gen(): for i in range(3): try: yield i except MyException: print("MyException!") a = gen() next(a) a.throw(MyException) 

Ejecutando este código:

 $ python3.3 main.py MyException! $ python3.3 main.py MyException! Exception TypeError: TypeError('catching classes that do not inherit from BaseException is not allowed',) in  ignored $ python3.3 main.py MyException! $ python3.3 main.py MyException! $ python3.3 main.py MyException! Exception TypeError: TypeError('catching classes that do not inherit from BaseException is not allowed',) in  ignored 

Lo que no entiendo es por qué a veces se imprime esta advertencia de Exception TypeError . ¿Hay algo mal con la excepción personalizada?

Usted está viendo un gancho __del__ mal __del__ algún lugar.

El TypeError se lanza mientras se apaga , ya que el intérprete de Python está saliendo y todo se borra y cualquier excepción lanzada en un gancho deconstructor __del__ se ignora (pero se imprime).

Al salir, Python borra todo en el espacio de nombres volviendo a vincular todo a None , pero el orden en que esto sucede no está establecido. El generador que aún está en ejecución está cerrado (se llama a.close() ) cuando se elimina, lo que desencadena una excepción de GeneratorExit en el generador, que Python prueba contra su línea except MyException: . Sin embargo, si MyException ya se ha borrado y Python ve except None: se lanza el TypeError y se ve ese mensaje impreso.

Puede desencadenar el error sin salir de Python agregando:

 MyException = None del a 

Si usa la list(a) y consume el rest del generador, o cierra explícitamente el generador con a.close() antes de que Python salga y elimine MyException , el mensaje de error desaparece.

Otra solución alternativa sería manejar GeneratorExit primero:

 def gen(): for i in range(3): try: yield i except GeneratorExit: return except MyException: print("MyException!") 

y Python no evaluará el siguiente, except controlador.

El error no se puede reproducir con Python 3.2 o anterior, por lo que parece que la aleatorización de hash (introducida en Python 3.3) aleatoriamente se borran los objetos de orden; Esto explica por qué solo ve el error en algunas de sus ejecuciones, pero no en las anteriores ejecuciones de Python donde se corrige el orden de hash.

Tenga en cuenta que la interacción de .__del__() ganchos y otros objetos globales en Python está documentada con una gran advertencia roja en la documentación de .__del__() :

Advertencia : debido a las circunstancias precarias en las que se __del__() métodos __del__() , se ignoran las excepciones que ocurren durante su ejecución y, en sys.stderr lugar, se imprime una advertencia a sys.stderr . Además, cuando se __del__() en respuesta a un módulo que se está eliminando (p. Ej., Cuando se realiza la ejecución del progtwig), es posible que otros __del__() los que __del__() referencia el __del__() ya se hayan eliminado o estén en proceso de ser demolidos ( Por ejemplo, apagando la maquinaria de importación). Por esta razón, los __del__() deben hacer el mínimo absoluto necesario para mantener invariantes externos. A partir de la versión 1.5, Python garantiza que los globales cuyo nombre comience con un solo guión bajo se eliminen de su módulo antes de que se eliminen otros globales. Si no existe ninguna otra referencia a dichos elementos globales, esto puede ayudar a garantizar que los módulos importados aún estén disponibles en el momento en que se __del__() método __del__() .

Estaba teniendo este mismo error en Python 3.3 en Windows, con la diferencia de que estaba definiendo la excepción en su propio archivo. Estos fueron mis archivos de código:

 $ cat FooError.py class FooError(Exception): pass $ cat application.py import FooError try: raise FooError('Foo not bar!') Except FooError as e: print(e) 

Esta fue la excepción que estaba recibiendo:

TypeError: las clases de captura que no se heredan de BaseException no están permitidas.

Cambiar la import FooError a la from FooError import * resolvió el problema. Aquí está el código final, para mayor claridad:

 $ cat FooError.py class FooError(Exception): pass $ cat application.py from FooError import * try: raise FooError('Foo not bar!') Except FooError as e: print(e) 

Tuve el mismo problema, pero faltaba la importación a la clase de excepción. Así que el intérprete no resolvió la clase en la cláusula de excepción.

Así que simplemente agregue la importación y ojalá todo funcione.