Python Language
Excepciones
Buscar..
Introducción
Los errores detectados durante la ejecución se denominan excepciones y no son incondicionalmente fatales. La mayoría de las excepciones no son manejadas por los programas; es posible escribir programas que manejen excepciones seleccionadas. Hay características específicas en Python para lidiar con las excepciones y la lógica de excepciones. Además, las excepciones tienen una jerarquía de tipos enriquecidos, todos heredados del tipo BaseException
.
Sintaxis
- levantar excepción
- elevar # re-elevar una excepción que ya ha sido planteada
- generar excepción de causa # Python 3 - establecer causa de excepción
- generar excepción desde Ninguna # Python 3 - suprimir todo el contexto de excepción
- tratar:
- excepto [tipos de excepción] [ como identificador ] :
- más:
- finalmente:
Levantando excepciones
Si su código encuentra una condición que no sabe cómo manejar, como un parámetro incorrecto, debe generar la excepción apropiada.
def even_the_odds(odds):
if odds % 2 != 1:
raise ValueError("Did not get an odd number")
return odds + 1
Atrapar excepciones
Utilice try...except:
para detectar excepciones. Debe especificar una excepción tan precisa como pueda:
try:
x = 5 / 0
except ZeroDivisionError as e:
# `e` is the exception object
print("Got a divide by zero! The exception was:", e)
# handle exceptional case
x = 0
finally:
print "The END"
# it runs no matter what execute.
La clase de excepción que se especifica, en este caso, ZeroDivisionError
, captura cualquier excepción que sea de esa clase o de cualquier subclase de esa excepción.
Por ejemplo, ZeroDivisionError
es una subclase de ArithmeticError
:
>>> ZeroDivisionError.__bases__
(<class 'ArithmeticError'>,)
Y así, lo siguiente seguirá ZeroDivisionError
:
try:
5 / 0
except ArithmeticError:
print("Got arithmetic error")
Ejecutando código de limpieza con finalmente
A veces, es posible que desee que ocurra algo, independientemente de la excepción que haya ocurrido, por ejemplo, si tiene que limpiar algunos recursos.
El bloque finally
de una cláusula de try
ocurrirá independientemente de si se produjeron excepciones.
resource = allocate_some_expensive_resource()
try:
do_stuff(resource)
except SomeException as e:
log_error(e)
raise # re-raise the error
finally:
free_expensive_resource(resource)
Este patrón a menudo se maneja mejor con los administradores de contexto (usando la declaración with
).
Re-elevando excepciones
A veces desea capturar una excepción solo para inspeccionarla, por ejemplo, para fines de registro. Después de la inspección, desea que la excepción continúe propagándose como lo hizo antes.
En este caso, simplemente use la instrucción raise
sin parámetros.
try:
5 / 0
except ZeroDivisionError:
print("Got an error")
raise
Sin embargo, tenga en cuenta que alguien que se encuentre más arriba en la pila de personas que llaman todavía puede detectar la excepción y manejarla de alguna manera. La salida realizada podría ser una molestia en este caso porque ocurrirá en cualquier caso (capturado o no capturado). Por lo tanto, podría ser una mejor idea plantear una excepción diferente, que contenga su comentario sobre la situación, así como la excepción original:
try:
5 / 0
except ZeroDivisionError as e:
raise ZeroDivisionError("Got an error", e)
Pero esto tiene el inconveniente de reducir la traza de excepción exactamente a este raise
mientras que la raise
sin argumento conserva la traza de excepción original.
En Python 3 puedes mantener la pila original usando la sintaxis de raise
- from
:
raise ZeroDivisionError("Got an error") from e
Cadena de excepciones con aumento de
En el proceso de manejar una excepción, es posible que desee plantear otra excepción. Por ejemplo, si obtiene un IOError
mientras lee un archivo, es posible que desee plantear un error específico de la aplicación para que se presente a los usuarios de su biblioteca.
Puede encadenar excepciones para mostrar cómo procedió el manejo de las excepciones:
>>> try:
5 / 0
except ZeroDivisionError as e:
raise ValueError("Division failed") from e
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
ZeroDivisionError: division by zero
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
ValueError: Division failed
Jerarquía de excepciones
El manejo de excepciones se produce en función de una jerarquía de excepciones, determinada por la estructura de herencia de las clases de excepciones.
Por ejemplo, IOError
y OSError
son subclases de EnvironmentError
. El código que captura un IOError
no detectará un OSError
. Sin embargo, el código que atrapa un EnvironmentError
detectará tanto IOError
s como OSError
s.
La jerarquía de las excepciones incorporadas:
BaseException
+-- SystemExit
+-- KeyboardInterrupt
+-- GeneratorExit
+-- Exception
+-- StopIteration
+-- StandardError
| +-- BufferError
| +-- ArithmeticError
| | +-- FloatingPointError
| | +-- OverflowError
| | +-- ZeroDivisionError
| +-- AssertionError
| +-- AttributeError
| +-- EnvironmentError
| | +-- IOError
| | +-- OSError
| | +-- WindowsError (Windows)
| | +-- VMSError (VMS)
| +-- EOFError
| +-- ImportError
| +-- LookupError
| | +-- IndexError
| | +-- KeyError
| +-- MemoryError
| +-- NameError
| | +-- UnboundLocalError
| +-- ReferenceError
| +-- RuntimeError
| | +-- NotImplementedError
| +-- SyntaxError
| | +-- IndentationError
| | +-- TabError
| +-- SystemError
| +-- TypeError
| +-- ValueError
| +-- UnicodeError
| +-- UnicodeDecodeError
| +-- UnicodeEncodeError
| +-- UnicodeTranslateError
+-- Warning
+-- DeprecationWarning
+-- PendingDeprecationWarning
+-- RuntimeWarning
+-- SyntaxWarning
+-- UserWarning
+-- FutureWarning
+-- ImportWarning
+-- UnicodeWarning
+-- BytesWarning
BaseException
+-- SystemExit
+-- KeyboardInterrupt
+-- GeneratorExit
+-- Exception
+-- StopIteration
+-- StopAsyncIteration
+-- ArithmeticError
| +-- FloatingPointError
| +-- OverflowError
| +-- ZeroDivisionError
+-- AssertionError
+-- AttributeError
+-- BufferError
+-- EOFError
+-- ImportError
+-- LookupError
| +-- IndexError
| +-- KeyError
+-- MemoryError
+-- NameError
| +-- UnboundLocalError
+-- OSError
| +-- BlockingIOError
| +-- ChildProcessError
| +-- ConnectionError
| | +-- BrokenPipeError
| | +-- ConnectionAbortedError
| | +-- ConnectionRefusedError
| | +-- ConnectionResetError
| +-- FileExistsError
| +-- FileNotFoundError
| +-- InterruptedError
| +-- IsADirectoryError
| +-- NotADirectoryError
| +-- PermissionError
| +-- ProcessLookupError
| +-- TimeoutError
+-- ReferenceError
+-- RuntimeError
| +-- NotImplementedError
| +-- RecursionError
+-- SyntaxError
| +-- IndentationError
| +-- TabError
+-- SystemError
+-- TypeError
+-- ValueError
| +-- UnicodeError
| +-- UnicodeDecodeError
| +-- UnicodeEncodeError
| +-- UnicodeTranslateError
+-- Warning
+-- DeprecationWarning
+-- PendingDeprecationWarning
+-- RuntimeWarning
+-- SyntaxWarning
+-- UserWarning
+-- FutureWarning
+-- ImportWarning
+-- UnicodeWarning
+-- BytesWarning
+-- ResourceWarning
Las excepciones son objetos también
Las excepciones son solo objetos de Python normales que heredan de la BaseException
. Una secuencia de comandos de Python puede usar la instrucción raise
para interrumpir la ejecución, lo que hace que Python imprima un seguimiento de pila de la pila de llamadas en ese punto y una representación de la instancia de excepción. Por ejemplo:
>>> def failing_function():
... raise ValueError('Example error!')
>>> failing_function()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in failing_function
ValueError: Example error!
que dice que un ValueError
con el mensaje 'Example error!'
fue planteado por nuestro failing_function()
, que fue ejecutado en el intérprete.
El código de llamada puede elegir manejar cualquier excepción de todo tipo que una llamada pueda generar:
>>> try:
... failing_function()
... except ValueError:
... print('Handled the error')
Handled the error
Puede obtener los objetos de excepción asignándolos en la parte except...
del código de manejo de excepciones:
>>> try:
... failing_function()
... except ValueError as e:
... print('Caught exception', repr(e))
Caught exception ValueError('Example error!',)
Puede encontrar una lista completa de las excepciones de Python incorporadas junto con sus descripciones en la Documentación de Python: https://docs.python.org/3.5/library/exceptions.html . Y aquí está la lista completa organizada jerárquicamente: Jerarquía de excepciones .
Creación de tipos de excepción personalizados
Crea una clase heredada de Exception
:
class FooException(Exception):
pass
try:
raise FooException("insert description here")
except FooException:
print("A FooException was raised.")
u otro tipo de excepción:
class NegativeError(ValueError):
pass
def foo(x):
# function that only accepts positive values of x
if x < 0:
raise NegativeError("Cannot process negative numbers")
... # rest of function body
try:
result = foo(int(input("Enter a positive integer: "))) # raw_input in Python 2.x
except NegativeError:
print("You entered a negative number!")
else:
print("The result was " + str(result))
No atrapes todo!
Aunque a menudo es tentador atrapar todas las Exception
:
try:
very_difficult_function()
except Exception:
# log / try to reconnect / exit gratiously
finally:
print "The END"
# it runs no matter what execute.
O incluso todo (que incluye BaseException
y todos sus hijos, incluida la Exception
):
try:
even_more_difficult_function()
except:
pass # do whatever needed
En la mayoría de los casos es una mala práctica. Es posible que SystemExit
más de lo previsto, como SystemExit
, KeyboardInterrupt
y MemoryError
, cada uno de los cuales generalmente debe manejarse de manera diferente a los errores habituales del sistema o la lógica. También significa que no hay una comprensión clara de qué puede hacer mal el código interno y cómo recuperarse adecuadamente de esa condición. Si está detectando cada error, no sabrá qué error ocurrió o cómo solucionarlo.
Esto se conoce más comúnmente como "enmascaramiento de errores" y debe evitarse. Deje que su programa se bloquee en lugar de fallar silenciosamente o incluso peor, fallando en un nivel más profundo de ejecución. (Imagina que es un sistema transaccional)
Por lo general, estas construcciones se usan en el nivel más externo del programa, y registrarán los detalles del error para que el error se pueda corregir, o el error se pueda manejar más específicamente.
Atrapando múltiples excepciones
Hay algunas maneras de atrapar múltiples excepciones .
La primera es creando una tupla de los tipos de excepción que desea capturar y manejar de la misma manera. Este ejemplo hará que el código ignore las excepciones KeyError
y AttributeError
.
try:
d = {}
a = d[1]
b = d.non_existing_field
except (KeyError, AttributeError) as e:
print("A KeyError or an AttributeError exception has been caught.")
Si desea manejar diferentes excepciones de diferentes maneras, puede proporcionar un bloque de excepción separado para cada tipo. En este ejemplo, todavía KeyError
y AttributeError
, pero manejamos las excepciones de diferentes maneras.
try:
d = {}
a = d[1]
b = d.non_existing_field
except KeyError as e:
print("A KeyError has occurred. Exception message:", e)
except AttributeError as e:
print("An AttributeError has occurred. Exception message:", e)
Ejemplos prácticos de manejo de excepciones.
Entrada del usuario
Imagina que quieres que un usuario ingrese un número a través de una input
. Desea asegurarse de que la entrada es un número. Puedes usar try
/ except
de esto:
while True:
try:
nb = int(input('Enter a number: '))
break
except ValueError:
print('This is not a number, try again.')
Nota: Python 2.x usaría raw_input
en raw_input
lugar; la input
la función existe en Python 2.x pero tiene una semántica diferente. En el ejemplo anterior, la input
también aceptaría expresiones como 2 + 2
que se evalúan como un número.
Si la entrada no se pudo convertir en un entero, se ValueError
un ValueError
. Puedes atraparlo con except
. Si se plantea no es una excepción, break
salta fuera del bucle. Después del bucle, nb
contiene un entero.
Los diccionarios
Imagine que está iterando sobre una lista de enteros consecutivos, como el range(n)
, y tiene una lista de diccionarios d
que contiene información sobre cosas que hacer cuando encuentra algunos enteros en particular, por ejemplo, omita los d[i]
siguientes .
d = [{7: 3}, {25: 9}, {38: 5}]
for i in range(len(d)):
do_stuff(i)
try:
dic = d[i]
i += dic[i]
except KeyError:
i += 1
Se KeyError
un KeyError
cuando intente obtener un valor de un diccionario para una clave que no existe.
Más
El código en un bloque else solo se ejecutará si el código en el bloque try
no genera excepciones. Esto es útil si tiene algún código que no desea ejecutar si se lanza una excepción, pero no quiere que se detecten excepciones lanzadas por ese código.
Por ejemplo:
try:
data = {1: 'one', 2: 'two'}
print(data[1])
except KeyError as e:
print('key not found')
else:
raise ValueError()
# Output: one
# Output: ValueError
Tenga en cuenta que este tipo de else:
no se puede combinar con if
inicia la cláusula else en un elif
. Si usted tiene un siguiente if
que necesita para mantenerse con sangría debajo de esa else:
:
try:
...
except ...:
...
else:
if ...:
...
elif ...:
...
else:
...