Solución:
¿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.')
No plantee excepciones genéricas
Evite levantar un genérico Exception
. Para detectarlo, tendrá que detectar todas las demás excepciones más específicas que lo subclasifiquen.
Problema 1: Ocultar errores
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',)
Problema 2: No se engancha
Y capturas más específicas no detectará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
Mejores prácticas: raise
declaración
En su lugar, utilice el constructor de excepciones más específico que se ajuste semánticamente a su problema.
raise ValueError('A very specific bad thing happened')
que también permite pasar un número arbitrario de argumentos al constructor:
raise ValueError('A very specific bad thing happened', 'foo', 'bar', 'baz')
Estos argumentos son accedidos por el args
atributo en el Exception
objeto. 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, un message
el atributo fue agregado a BaseException
a favor de alentar a los usuarios a subclasificar Excepciones y dejar de usar args
, pero la introducción de message
y la desaprobación original de los argumentos se ha retirado.
Mejores prácticas: except
cláusula
Cuando 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 generar. La mejor manera de hacer esto mientras se conserva el seguimiento de la pila es usar una declaración de aumento 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!
No modifiques tus errores … pero si insistes.
Puede conservar el seguimiento de la pila (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, prefiero usar un desnudo raise
volver a subir.
Para explicar – el 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 un nuevo args
por ejemplo:
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 conservado todo el rastreo mientras modificamos los argumentos. Tenga en cuenta que esto es no es una buena práctica y es sintaxis inválida en Python 3 (lo que hace que mantener la compatibilidad sea mucho más difícil de evitar).
>>> 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 los rastreos manualmente. Es menos eficiente y más propenso a errores. Y si estás usando subprocesos y sys.exc_info
incluso puede obtener un seguimiento incorrecto (especialmente si está utilizando el manejo de excepciones para el flujo de control, que personalmente tendería a evitar).
Python 3, encadenamiento de excepciones
En Python 3, puede encadenar Excepciones, que conservan los rastreos:
raise RuntimeError('specific message') from error
Tenga en cuenta:
- esta lo hace permitir cambiar el tipo de error planteado, y
- este es no compatible con Python 2.
Métodos obsoletos:
Estos pueden esconderse fácilmente e incluso ingresar al código de producción. Desea generar una excepción y, al hacerlo, se generará una excepción, ¡pero no el previsto!
Válido en Python 2, pero no en Python 3, es lo siguiente:
raise ValueError, 'message' # Don't do this, it's deprecated!
Solo es válido en versiones mucho más antiguas de Python (2.4 y anteriores), es posible que aún veas personas subiendo cadenas:
raise 'message' # really really wrong. don't do this.
En todas las versiones modernas, esto generará un TypeError
, porque no estás levantando un BaseException
escribe. Si no está buscando la excepción correcta y no tiene un revisor que esté al tanto del problema, podría entrar en producción.
Ejemplo de uso
Genero 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)))
Cree sus propios tipos de error cuando sea apropiado
“Quiero cometer un error a propósito, para que entre en el excepto”
Puede crear sus propios tipos de error, si desea indicar que algo específico está mal en su aplicación, simplemente subclase el punto apropiado en la jerarquía de excepciones:
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.')
NO HAGAS ESTO. Levantando un desnudo
Exception
es absolutamente no lo correcto a hacer; en su lugar, consulte la excelente respuesta de Aaron Hall.
No puedo ser mucho más pitónico que esto:
raise Exception("I know python!")
Consulte los documentos de la declaración de aumento para Python si desea obtener más información.
En Python3 hay 4 sintaxis diferentes para las excepciones rasantes:
1. raise exception
2. raise exception (args)
3. raise
4. raise exception (args) from original_exception
1. generar excepción frente a 2. generar excepción (argumentos)
Si utiliza raise exception (args)
para plantear una excepción, entonces el args
se imprimirá cuando imprima el objeto de excepción, como se muestra en el ejemplo siguiente.
#raise exception (args)
try:
raise ValueError("I have raised an Exception")
except ValueError as exp:
print ("Error", exp) # Output -> Error I have raised an Exception
#raise execption
try:
raise ValueError
except ValueError as exp:
print ("Error", exp) # Output -> Error
3.aumentar
raise
declaración sin ningún argumento vuelve a generar la última excepción. Esto es útil si necesita realizar algunas acciones después de detectar la excepción y luego desea volver a generarla. Pero si no hubo excepción antes, raise
declaración eleva TypeError
Excepción.
def somefunction():
print("some cleaning")
a=10
b=0
result=None
try:
result=a/b
print(result)
except Exception: #Output ->
somefunction() #some cleaning
raise #Traceback (most recent call last):
#File "python", line 8, in <module>
#ZeroDivisionError: division by zero
4. generar una excepción (argumentos) de original_exception
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 siguiente ejemplo.
class MyCustomException(Exception):
pass
a=10
b=0
reuslt=None
try:
try:
result=a/b
except ZeroDivisionError as exp:
print("ZeroDivisionError -- ",exp)
raise MyCustomException("Zero Division ") from exp
except MyCustomException as exp:
print("MyException",exp)
print(exp.__cause__)
Producción:
ZeroDivisionError -- division by zero
MyException Zero Division
division by zero