IBM SkillsBuild | Introducción a Python
Conceptos básicos y
sintaxis de Python
Excepciones
1
IBM SkillsBuild | Introducción a Python
Índice
Introducción 3
Los roles de las excepciones en Python 4
Nuestra primera excepción 4
Capturando excepciones 5
Lanzando excepciones manualmente 7
La sentencia `assert` 8
Creando nuestras propias excepciones 8
Acciones de finalización y limpieza 9
El bloque `else` en las excepciones 11
2
IBM SkillsBuild | Introducción a Python
Introducción
Las excepciones son eventos que permiten controlar
el flujo de un programa cuando ocurre un error.
Pueden dispararse automáticamente, cuando ocurre
un error y bajo demanda en nuestro código. Es
posible capturar una excepción para corregir el error
que la ha originado o escalar la misma hacia arriba
para que la intercepte otro código de más alto nivel.
3
IBM SkillsBuild | Introducción a Python
Los roles de las
Primero creamos una función, que devuelve el
elemento que le pidamos de una secuencia según el
excepciones en Python índice que le pidamos:
def indexador(objeto, indice):
return objeto[indice]
• Manejo de errores: Se utilizan para informar de
errores y/o de una situación anómala así como de indexador('Python', 0)
detener el flujo de programa.
• Notificación de eventos: P. ej. terminar una
búsqueda sin resultados sin tener que usar Resultado:
variables de control.
• Manejo de casos especiales: Podemos dejar el
manejo de algunas situaciones especiales que
ocurren con poca frecuencia a excepciones.
• Acciones de limpieza/finalización: Operaciones
de limpieza que se ejecutan tanto como si ha Como vemos, si le pedimos un índice que existe en la
habido errores como si no y que nos ayudan a secuencia nos devuelve el elemento alojado en ese
asegurarnos que este tipo de operaciones índice. En cambio, si pedimos un índice demasiado
ocurren siempre, independientemente de que grande, veremos que obtenemos un error de tipo
haya habido un error o no. Esto es útil para IndexError.
asegurarnos que cerramos una conexión, un
def indexador(objeto, indice):
fichero, etc.
return objeto[indice]
En Python disponemos de 4 sentencias que podemos
indexador('Python', 10) #Produce un error
utilizar para manejar excepciones:
• try/except: Intercepta y recupera excepciones
Resultado:
disparadas por Python o por nuestro código.
• try/finally: Realiza tareas de limpieza ocurran
las excepciones o no.
• raise: Dispara una excepción manualmente en el
código.
• assert: Dispara una excepción
condicionalmente.
En esta unidad veremos cuándo utilizar cada una de
estas sentencias, así como ejemplos de las mismas.
Nuestra primera excepción
A continuación, vamos a crear un código que
generará una excepción si se le introduce un
parámetro adecuado.
4
IBM SkillsBuild | Introducción a Python
Notad que en otras unidades hemos simplificado las Resultado:
salidas de las excepciones, en este no lo haremos
para que podáis ver toda la información relativa al
error que ha ocasionado la excepción.
Por ejemplo, en la llamada que acabamos de hacer,
el intérprete ha lanzado la excepción, ha detenido el
programa, y además nos ha mostrado información a
la excepción:
En el bloque try se incluye el código propenso a
1. Primero muestra el tipo de la excepción:
causar la Excepción que queremos capturar en la
IndexError
sentencia except. La sentencia except está compuesta
2. Luego nos muestra la traza de la misma, es decir
por la palabra clave que da nombre a la sentencia
la cadena de llamadas que la ha producido y la
junto con la clase de la excepción que queremos
línea de código donde se ha producido
capturar.
3. Finalmente, el intérprete nos muestra de nuevo
el tipo de excepción y una cadena de Por ejemplo, IndexError es una excepción que ocurre
información describiendo el error: ‘IndexError: cuando intentamos acceder al índice de una
string index out of range’. En este caso nos secuencia y este índice está fuera de rango.
explica que el índice de la cadena está fuera de
rango. Dentro del bloque except incluimos el código
necesario para manejar la situación cuando
Notad que diferentes intérpretes pueden daros la
capturamos la excepción. En el ejemplo,
información relativa a una excepción con un formato
simplemente estamos notificando en pantalla que el
ligeramente diferente, pero en esencia, casi siempre
usuario ha puesto un índice fuera de rango. Notad
muestran los tres campos que acabamos de
que sólo entramos en el except en caso que salte
mencionar.
excepción IndexError:
def indexador(objeto, indice):
Capturando excepciones return objeto[indice]
try:
Para capturar excepciones utilizamos el bloque de print(indexador('Python', 3))
sentencias try/except: except IndexError: # Captura
Indexerror
def indexador(objeto, indice): print('Has puesto un índice muy
return objeto[indice] grande.')
print('Hemos salido del try-catch')
try:
indexador('Python', 10) Resultado:
except IndexError: # Captura
Indexerror
print('Has puesto un índice muy
grande.')
print('Hemos salido del try-catch')
5
IBM SkillsBuild | Introducción a Python
Un aspecto que es importante es que el except sólo La segunda opción, es válida sintácticamente en
captura las excepciones indicadas en la sentencia. Es Python, pero está menos recomendada porque
decir, si dentro del try se produce una excepción no captura cualquier excepción. Esto es peligroso
contemplada en el except, no seremos capaces de porque podríamos estar silenciando excepciones no
capturarla. Eso sí, es posible capturar varios tipos de previstas por nosotros, por lo que estaríamos
excepciones a la vez: ocultando errores que luego serán difíciles de
encontrar. Utilizad esta segunda opción con mucho
def indexador(objeto, indice): cuidado y conociendo el riesgo al que os exponéis.
return objeto[indice]
Una limitación de las dos aproximaciones
try: anteriores es que, a pesar de que somos capaces
indexador('Python', 'h') de interceptar las excepciones indicadas, las
except (IndexError, TypeError): #
estamos tratando indistintamente. Es decir, que
Captura varios errores
vamos a tratar la excepción capturada de la
print('Error.')
misma manera independientemente que fuera
print('Hemos salido del try-catch')
una u otra. Si queremos hacer un tratamiento
try: especial a un tipo de excepciones, lo podemos
indexador('Python', 'h') hacer encadenando bloques except uno detrás de
except: # Captura todos los errores. otro, de manera similar a bloques elif en los
No recomendado. condicionales:
print('Error.')
print('Hemos salido del try-catch') def indexador(objeto, indice):
return objeto[indice]
try:
Resultado: indexador('Python', 'h')
except IndexError: # Captura
IndexError
print('Error de índice.')
except TypeError: # Captura
TypeError
print('El índice tiene que ser un
número.')
print('Hemos salido del try-catch')
En este código estamos mostrando dos
maneras de interceptar varias excepciones.
La primera es la que nosotros recomendamos Resultado:
y consiste en un listado (una tupla) de
Excepciones que pueden saltar en nuestro
bloque try. Cuando salte cualquiera de las
Excepciones indicadas en nuestra tupla,
entraremos en el bloque except.
6
IBM SkillsBuild | Introducción a Python
En este caso, hemos llamado a indexador Si queremos añadir esa información, simplemente
produciendo una excepción de tipo TypeError. creamos una instancia de IndexError (o de la
Estas excepciones saltan cuando intentamos hacer excepción que queramos lanzar) y en su constructor
una operación no admitida por el tipo que estamos añadimos el mensaje a mostrar:
utilizando. En este caso hemos intentado acceder al
índice de una cadena de texto con otra cadena de
raise IndexError('Excepción lanzada
texto en lugar de un número, por lo que hemos
manualmente')
producido una excepción de tipo.
Lanzando excepciones Resultado:
manualmente
Podemos lanzar excepciones directamente en
nuestro código utilizando la sentencia raise seguida
del tipo de excepción que queremos lanzar. Por
ejemplo:
raise IndexError Ahora sí, vemos que en la última línea del error
tenemos el mensaje explicativo del error.
Por supuesto, es posible capturar nuestras propias
Resultado:
excepciones lanzadas manualmente:
try:
raise IndexError('Excepción lanzada
manualmente')
except:
print('He capturado mi pripia
excepción')
Resultado:
Aquí acabamos de lanzar nuestra propia excepción
de tipo IndexError. Eso sí, en este caso, al ver la traza
del error, no tenemos ninguna información que nos
oriente cual ha podido ser la causa del error.
7
IBM SkillsBuild | Introducción a Python
La sentencia ‘assert’
Además de lanzar excepciones manualmente, es
posible hacerlo condicionalmente. Para ello, Python
proporciona la sentencia assert que nos permite
lanzar una excepción si se cumple una condición
determinada.
Es muy común utilizar la sentencia assert durante el
proceso de depuración, para asegurarnos que se Una nota importante sobre esta la sentencia assert:
cumplen ciertas condiciones previas. su uso es muy útil para detectar errores en
depuración, pero no se recomienda el uso de assert
La sintaxis de assert es la siguiente:
en producción.
assert(condición), ‘Mensaje de error’
Veamos un ejemplo:
Creando nuestras propias
a = 10 excepciones
b = 0
assert(a > b), 'A tiene que ser mayor que
Hasta ahora hemos visto como capturar y lanzar
B!' # Si se cumple no salta el error
excepciones incluidas en la librería estándar de
print('Si se muestra esto es que no ha Python. Sin embargo, en muchos casos es de gran
saltado el assert') utilidad crear nuestras propias excepciones.
Si no os habíais fijado hasta ahora, las excepciones
Resultado: son clases. Por eso, si queremos crear nuestra propia
excepción, sólo tenemos que crear una clase que
herede de la clase base de todas las excepciones
(Exception) o de cualquier otra excepción:
class miPropiaExcepcion(Exception): #Las
En este caso, como se cumple la condición, no salta excepciones heredan de Exception
el assert, y el programa sigue ejecutándose pass
normalmente. Veamos un caso en el que sí salta la raise miPropiaExcepcion
excepción producida por el assert:
a = 10
b = 0
c = 5
assert(a == c), 'A y C tienen que ser
iguales!'
Resultado:
8
IBM SkillsBuild | Introducción a Python
Resultado: Resultado:
En este ejemplo hemos creado un constructor de
Acabamos de crear la clase MiPropiaExcepcion que nuestra excepción que utilizamos para almacenar un
hereda de Exception. Cuando una clase hereda de objeto que luego pasaremos al método __str__. Este
Exception, podemos incluirla dentro de una sentencia método es un método especial de Python, llamado
raise para lanzarla, así como dentro de un except para “método mágico”. En concreto, __str__ define como
interceptarla: hay que representar una clase si se la quiere mostrar
como un string (una cadena de texto), por ejemplo,
class miPropiaExcepcion(Exception): #Las
excepciones heredan de Exception para introducirla en un print, etc.
pass
En este caso, el método __str__ permite mostrar el
try: mensaje de error que queramos al lanzar nuestra
raise miPropiaExcepcion
excepción.
except miPropiaExcepcion:
print('He capturado mi propia
excepción') Acciones de finalización y
Resultado:
limpieza
Cuando tenemos excepciones, hay situaciones en las
que queremos hacer operaciones de limpieza o
finalización sin importar si la excepción ha saltado o
Pero nuestra excepción es muy básica. Vamos a no. Este tipo de operaciones suelen ser, por ejemplo,
mejorarla un poco para que pueda representar su asegurarnos que cerramos un fichero, una conexión,
propio mensaje de error: etc.
class miError(Exception): Para esto tenemos la sentencia finally:
def __init__(self, valor): def indexador(objeto, indice):
[Link] = valor return objeto[indice]
def __str__(self): try:
return str([Link]) indexador('Python', 10)
finally:
raise(miError('Mensaje de error')) print('Esto se ejecuta includo cuando
salta la excepción')
9
IBM SkillsBuild | Introducción a Python
Resultado: Notad también que el finally se ejecuta siempre,
salte o no salte la excepción, pero si la excepción ha
saltado, el flujo de programa se detiene justo
después del finally.
def indexador(objeto, indice):
return objeto[indice]
try:
indexador('Python', 10)
finally:
print('Esto se ejecuta includo cuando
salta la excepción')
print('Este print tampoco se ejecuta')
Resultado:
En este código hemos llamado a indexador de
manera errónea y hemos producido una excepción.
Normalmente, cuando esto ocurre, se detiene el flujo
de programa. En este caso, al tener un bloque finally
lo que ocurre es que, justo antes de detenerse el flujo
de programa, ejecutamos el código que esté incluido
en nuestro bloque finally.
Notad que el código siguiente, se le parece, pero NO
se ejecutará:
def indexador(objeto, indice):
return objeto[indice]
Es decir, que el finally sólo asegura que el código de
su bloque se va a ejecutar, pero impide que el flujo
try:
indexador('Python', 10) de programa se detenga. Para eso, recordad que
print('Este print no se ejecuta') tenemos el bloque except:
finally:
def indexador(objeto, indice):
print('Esto se ejecuta includo cuando
return objeto[indice]
salta la excepción') try:
indexador('Python', 10)
except IndexError:
En este caso vemos que el print no se ha ejecutado print('Capturamos la excepción')
ya que antes ha saltado la excepción y, por lo tanto, finally:
print('Esto se ejecuta incluso cuando
se ha detenido el flujo de ejecución del programa. salta la excepción')
print('Se ejecutará este print?')
10
IBM SkillsBuild | Introducción a Python
Resultado: Resultado:
En este caso, indexador produce una excepción,
En este ejemplo intentamos hacer una división, y
que capturamos en el bloque except, por lo que
controlamos dentro de un bloque try/except si
ejecutamos el código dentro de ese bloque.
hemos intentado hacer una división por 0. Si el
Luego, ejecutamos el código finally y, tras ello,
usuario intenta dividir por 0, capturamos la
como ejecutamos el código fuera de nuestro
excepción en el except. Si la operación es correcta,
bloque try/except/finally.
entonces mostramos el resultado en el bloque else.
La ventaja del bloque else nos ahorra tener que
El bloque 'else' en las evaluar si tenemos resultado o no (podríamos no
excepciones haber obtenido un resultado en el caso de división
por cero).
La última sentencia útil en el uso de excepciones es
la sentencia else. En el caso de excepciones, la
sentencia else se comporta de manera muy parecida
a cómo lo hacía al ponerla tras un bucle: ejecuta el
código de su bloque sólo si NO salta la excepción en
el bloque try/except:
def divide(x, y):
try:
resultado = x/y
except ZeroDivisionError:
print('No se puede dividir por
cero')
else:
print('El resultado es: ')
finally:
print('Ejecutamos el finally')
divide(4, 12)
divide(4, 0)
11