¿Cómo puedo generar una excepción en Python para que luego pueda capturarse a través de un except
bloque?
¿Cómo lanzo / levanto manualmente una excepción en Python?
Utilice el constructor de excepciones más específico que se ajuste semánticamente a su problema .
Sea específico en su mensaje, por ejemplo:
raise ValueError('A very specific bad thing happened.')
Evita criar un genérico Exception
. Para atraparlo, deberá atrapar todas las demás excepciones más específicas que lo subclasifiquen.
raise Exception('I know Python!') # Don't! If you catch, likely to hide bugs.
Por ejemplo:
def demo_bad_catch():
try:
raise ValueError('Represents a hidden bug, do not catch this')
raise Exception('This is the exception you expect to handle')
except Exception as error:
print('Caught this error: ' + repr(error))
>>> demo_bad_catch()
Caught this error: ValueError('Represents a hidden bug, do not catch this',)
Y las capturas más específicas no atraparán la excepción general:
def demo_no_catch():
try:
raise Exception('general exceptions not caught by specific handling')
except ValueError as e:
print('we will not catch exception: Exception')
>>> demo_no_catch()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in demo_no_catch
Exception: general exceptions not caught by specific handling
raise
declaraciónraise ValueError('A very specific bad thing happened')
que también permite fácilmente pasar un número arbitrario de argumentos al constructor:
raise ValueError('A very specific bad thing happened', 'foo', 'bar', 'baz')
El args
atributo en el Exception
objeto accede a estos argumentos . Por ejemplo:
try:
some_code_that_may_raise_our_value_error()
except ValueError as err:
print(err.args)
huellas dactilares
('message', 'foo', 'bar', 'baz')
En Python 2.5, message
se agregó un atributo real para BaseException
alentar a los usuarios a subclase Excepciones y dejar de usar args
, pero la introducción message
y la desaprobación original de args se ha retraído .
except
cláusulaCuando esté dentro de una cláusula except, es posible que desee, por ejemplo, registrar que ocurrió un tipo específico de error y luego volver a subir. La mejor manera de hacer esto mientras se preserva el seguimiento de la pila es usar una declaración de subida simple. Por ejemplo:
logger = logging.getLogger(__name__)
try:
do_something_in_app_that_breaks_easily()
except AppError as error:
logger.error(error)
raise # just this!
# raise AppError # Don't do this, you'll lose the stack trace!
Puede preservar el stacktrace (y el valor de error) con sys.exc_info()
, pero esto es mucho más propenso a errores y tiene problemas de compatibilidad entre Python 2 y 3 , prefiera usar un desnudo raise
para volver a subir.
Para explicar: sys.exc_info()
devuelve el tipo, el valor y el rastreo.
type, value, traceback = sys.exc_info()
Esta es la sintaxis en Python 2; tenga en cuenta que esto no es compatible con Python 3:
raise AppError, error, sys.exc_info()[2] # avoid this.
# Equivalently, as error *is* the second object:
raise sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]
Si lo desea, puede modificar lo que sucede con su nuevo aumento, por ejemplo, establecer nuevo args
para la instancia:
def error():
raise ValueError('oops!')
def catch_error_modify_message():
try:
error()
except ValueError:
error_type, error_instance, traceback = sys.exc_info()
error_instance.args = (error_instance.args[0] + ' <modification>',)
raise error_type, error_instance, traceback
Y hemos preservado todo el rastreo mientras modificamos los argumentos. Tenga en cuenta que esta no es una práctica recomendada y es una sintaxis no válida en Python 3 (por lo que es mucho más difícil evitar la compatibilidad).
>>> catch_error_modify_message()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in catch_error_modify_message
File "<stdin>", line 2, in error
ValueError: oops! <modification>
En Python 3 :
raise error.with_traceback(sys.exc_info()[2])
Nuevamente: evite manipular manualmente las trazas. Es menos eficiente y más propenso a errores. Y si está utilizando subprocesos e sys.exc_info
incluso puede obtener el rastreo incorrecto (especialmente si está utilizando el manejo de excepciones para el flujo de control, lo que personalmente tienden a evitar).
En Python 3, puede encadenar Excepciones, que preservan las trazas:
raise RuntimeError('specific message') from error
Se consciente:
Estos pueden esconderse fácilmente e incluso entrar en el código de producción. Desea generar una excepción, y al hacerlo, se generará una excepción, ¡ pero no la prevista!
Válido en Python 2, pero no en Python 3 es el siguiente:
raise ValueError, 'message' # Don't do this, it's deprecated!
Solo válido en versiones mucho más antiguas de Python (2.4 y versiones anteriores ), aún puede ver personas elevando cadenas:
raise 'message' # really really wrong. don't do this.
En todas las versiones modernas, esto en realidad aumentará a TypeError
, porque no estás elevando un BaseException
tipo. Si no está buscando la excepción correcta y no tiene un revisor que conozca el problema, podría entrar en producción.
Levanto Excepciones para advertir a los consumidores de mi API si la están usando incorrectamente:
def api_func(foo):
'''foo should be either 'baz' or 'bar'. returns something very useful.'''
if foo not in _ALLOWED_ARGS:
raise ValueError('{foo} wrong, use "baz" or "bar"'.format(foo=repr(foo)))
"Quiero cometer un error a propósito, para que entre en la excepción"
Puede crear sus propios tipos de error, si desea indicar que algo específico está mal con su aplicación, simplemente subclasifique el punto apropiado en la jerarquía de excepción:
class MyAppLookupError(LookupError):
'''raise this when there's a lookup error for my app'''
y uso:
if important_key not in resource_dict and not ok_to_be_missing:
raise MyAppLookupError('resource is missing, and that is not ok.')
raise
es lo que necesitaba para poder realizar una depuración de errores personalizada en múltiples niveles de ejecución de código sin romper el seguimiento de la pila.raise sys.exc_info()[0], (sys.exc_info()[1], my_extra_info), sys.exc_info()[2]
parece hacer lo que quiero, y nunca he tenido problemas con eso. Pero se siente hacky, y no es una práctica aceptada. ¿Hay una mejor manera?Exception
su clase principal: puede subclasificar algo más específico y debe hacerlo si tiene sentido.AppError
excepción indefinida . Puede ser mejor usar un error incorporado comoAttributeError
No puede ser mucho más pitónico que esto:
Consulte los documentos de la declaración de aumento para python si desea obtener más información.
fuente
En Python3 hay 4 sintaxis diferentes para excepciones de rasing:
Si utiliza
raise exception (args)
para generar una excepciónargs
, se imprimirá cuando imprima el objeto de excepción, como se muestra en el siguiente ejemplo.raise
declaración sin ningún argumento vuelve a plantear la última excepción. Esto es útil si necesita realizar algunas acciones después de detectar la excepción y luego desea volver a subirla. Pero si no hubo ninguna excepción antes, laraise
declaración generaTypeError
Excepción.Esta declaración se utiliza para crear un encadenamiento de excepciones en el que una excepción que se genera en respuesta a otra excepción puede contener los detalles de la excepción original, como se muestra en el ejemplo a continuación.
Salida:
fuente
exception(args)
másexception (args)
raise exception(args) from None
que decir que la excepción actualmente activa se manejó y ya no es de interés. De lo contrario, si genera una excepción dentro de unexcept
bloque y no se maneja, los rastreos para ambas excepciones se mostrarán separados por el mensaje "Durante el manejo de la excepción anterior, se produjo otra excepción"Para el caso común en el que necesita lanzar una excepción en respuesta a algunas condiciones inesperadas, y que nunca tiene la intención de atrapar, sino simplemente fallar rápidamente para permitirle depurar desde allí si alguna vez sucede, la más lógica parece ser
AssertionError
:fuente
ValueError
queAssertionError
porque no hay problema con una afirmación (porque no se está haciendo ninguna aquí), el problema es con un valor. Si realmente quieres unAssertionError
en este caso, escribeassert distance > 0, 'Distance must be positive'
. Pero no debe verificar los errores de esa manera porque las aserciones se pueden desactivar (python -O
).-O
.Lea las respuestas existentes primero, esto es solo un apéndice.
Tenga en cuenta que puede generar excepciones con o sin argumentos.
Ejemplo:
sale del programa, pero es posible que desee saber qué sucedió, por lo que puede usar esto.
esto imprimirá "programa salido" en stderr antes de cerrar el programa.
fuente
raise SystemExit()
sería la mejor opción? ¿Por qué el primero incluso funciona?Otra forma de lanzar una excepción es
assert
. Puede usar afirmar para verificar que se está cumpliendo una condición, de lo contrario, aumentaráAssertionError
. Para más detalles echa un vistazo aquí .fuente
Solo para tener en cuenta: hay momentos en los que NO desea manejar excepciones genéricas. Si está procesando un montón de archivos y registrando sus errores, es posible que desee detectar cualquier error que ocurra para un archivo, registrarlo y continuar procesando el resto de los archivos. En ese caso, un
bloquear una buena forma de hacerlo. Sin embargo, aún querrás
raise
excepciones específicas para que sepas lo que significan.fuente
Deberías aprender la declaración raise de python para eso. Debe mantenerse dentro del bloque de prueba. Ejemplo
fuente