Programar Juegos Arcade
con Python y Pygame

Chapter 18: Excepciones

Vídeo: Gestión de Excepciones

¿Que te parecería si, por ejemplo, tu programa no funcionara bien, evitar que el usuario viese los típicos mensajes rojos de error de Python, o que el programa no se colgara? Si es así, lo que necesitas son excepciones.

Las excepciones las usamos para gestionar condiciones anormales que puedan aparecer durante la ejecución del código. Habitualmente se usan para operaciones de red o sobre archivos. Esto permite que el código salga airoso si ocurre, por ejemplo, un desbordamiento del disco, un error de red o problemas con determinados permisos.

18.1 Vocabulario

Los términos y frases más habituales cuando trabajamos con excepciones son:


La mayoría de lenguajes usan términos como “lanzar” y “capturar.” Desafortunadamente no es el caso de Python. Python emplea “levantar” y “excepción.” Nosotros hemos introducido los términos lanzar/capturar porque son los más utilizados en la industria.

18.2 Gestión de Excepciones

El código para manejar las excepciones es sencillo. Observa el siguiente ejemplo:

# Dividir entre cero
try:
    x = 5 / 0
except:
    print("Error división entre cero")

Sobre la línea 2 tenemos la sentencia try. Todas las líneas indentadas siguientes pertenecen al “bloque try.” Debajo del bloque try no debería existir ningún código no-indentado que no empezara con la sentencia except. La sentencia try define una sección que el código intentará ejecutar.

Si apareciera cualquier excepción durante el procesamiento del código, la ejecución saltaría inmediatamente hacia el “bloque de captura” (“catch block.”) Esta parte del bloque se halla indentada bajo la sentencia except de la línea 4. Éste es el código responsable de manejar el error.

Podemos tener un programa que use excepciones para capturar errores que puedan aparecer durante la conversión de un texto a número, como por ejemplo:

# Conversión fallida a número
try:
    x = int("fred")
except:
    print ("Error al intentar convertir fred a número")

En la línea 3 se lanzará una excepción debido a que “fred” no puede ser convertido a número entero. El código de la línea 5 imprimirá el mensaje de error.

Debajo podemos ver una versión ampliada del ejemplo anterior. En este caso se comprueba si existe algún error en la entrada de usuario, asegurándose de que se ha introducido un número entero. Si el usuario no lo introduce, el programa continuará demandándole que lo haga. El código utiliza una excepción para capturar un posible error de conversión que pudiera ocurrir en la línea 5. Si se introdujera cualquier otra cosa que no fuera un entero, se lanzaría una excepción al intentarse la conversión en la línea 5. Si así fuera, el código de la línea 6, que establece numero_introducido a True, no se ejecutaría.

numero_introducido = False
while not numero_introducido:
    numero_cadena = input("Introduce un número entero: ")
    try:
        n = int(numero_cadena)
        numero_introducido = True
    except:
        print ("Error, número entero incorrecto")

Los archivos son particularmente proclives a errores durante las operaciones con ellos; el disco podría llenarse por completo, un usuario podría borrar un archivo mientras se escribe sobre él, o la unidad USB ser extraída en mitad de una operación de lectura/escritura. Este tipo de errores pueden ser fácilmente capturados usando excepciones.

# Error al abrir un archivo
try:
    mi_archivo = open("miarchivo.txt")
except:
    print("Error al abrir el archivo")

Podemos capturar y procesar, de maneras distintas, muchos tipos de errores. Por ejemplo, sería muy útil mostrarle al usuario un mensaje de error más preciso que un simple; “ocurrió un error.”

En el siguiente ejemplo, entra las líneas 5-8, podemos encontrarnos con diferentes tipos de errores. Si colocamos un IOError luego de un except en la línea 9, solo los errores referidos a Entradas y Salidas (Input/Output) serán manejados por esta parte del código. Igualmente, la línea 11 solo maneja errores referidos a conversión de valores, y la línea 13, errores producidos por la división entre cero. La última excepción ocurre en la línea 15. Como en esta línea no se incluye ningún tipo particular de error, en ella se gestionarán aquellos errores no cubiertos por los anteriores bloques except. La sentencia except “captura-todo” debe ser siempre la última.

En la línea 1 se importa la biblioteca sys, que será utilizada en la línea 16 para imprimir el tipo de error ocurrido.

import sys

# Errores múltiples
try:
    mi_archivo = open("miarchivo.txt")
    mi_linea = mi_archivo.readline()
    mi_entero = int(s.strip())
    mi_valor_calculado = 101 / mi_entero
except IOError:
    print("I/O error")
except ValueError:
    print("No he podido convertir el dato a número entero.")
except ZeroDivisionError:
    print("División entre cero, error")
except:
    print("Error Inesperado:", sys.exc_info()[0])

Una lista de las excepciones implementadas en Python se encuentra disponible en la siguiente dirección web:
http://docs.python.org/library/exceptions.html

18.3 Ejemplo: Almacenar La Máxima Puntuación

Aquí mostraremos cómo guardar la máxima puntuación (high score) entre juegos. Esta puntuación será almacenada en un archivo llamado high_score.txt.

"""
# Sample Python/Pygame Programs
# Simpson College Computer Science
# http://programarcadegames.com/
# http://simpson.edu/computer-science/
"""

def obtener_puntuacion_mas_alta():
    # Puntuación más alta por defecto
    puntuacion_mas_alta = 0

    # Intentemos leer la puntuación más alta desde un archivo
    try:
        archivo_puntuacion_mas_alta = open("high_score.txt", "r")
        puntuacion_mas_alta = int(archivo_puntuacion_mas_alta.read())
        archivo_puntuacion_mas_alta.close()
        print("La puntuación más alta es", puntuacion_mas_alta)
    except IOError:
        # Error al leer el archivo, no existe una puntuación más alta
        print("Aún no existe una puntuación más alta.")
    except ValueError:
        # Hay un archivo allí, pero no entiendo los números.
        print("Estoy confundido. Empezamos sin una puntuación alta.")

    return puntuacion_mas_alta

def guardar_puntuacion_mas_alta(nueva_puntuacion_mas_alta):
    try:
        # Escribimos el archivo en disco
        archivo_puntuacion_mas_alta = open("high_score.txt", "w")
        archivo_puntuacion_mas_alta.write(str(nueva_puntuacion_mas_alta))
        archivo_puntuacion_mas_alta.close()
    except IOError:
        # Um, no puedo escribirlo.
        print("No soy capaz de guardar la puntuación alta.")
    
def main():
    """ Aquí va el programa principal. """
    # Obtenemos la puntuación más alta.
    puntuacion_mas_alta = obtener_puntuacion_mas_alta()
    
# Obtenemos la puntuación del juego en curso
    puntuacion_actual = 0
    try:
        # Pregúntale al usuario/a por su puntuación
        puntuacion_actual = int(input ("¿Cuál es tu puntuación? "))
    except ValueError:
        # Error, no puedo convertir lo que ha escrito en un número
        print("No comprendo qué has escrito.")
        

    # Observa por si tenemos una nueva puntuación más alta
    if puntuacion_actual > puntuacion_mas_alta:
        # Conseguido! Guardamos en disco
        print("Bravo! Nueva puntuación más alta!")
        guardar_puntuacion_mas_alta(puntuacion_actual)
    else:
        print("Mejor suerte la próxima vez.")

# Llamamos a la función principal, empezamos el juego
if __name__ == "__main__":
    main()

18.4 Objetos del tipo Excepción

A través de un objeto excepción, podemos extraer más información acerca de un error. Este objeto puede ser recogido mientras capturamos un error, usando la palabra clave as. Por ejemplo:

try:
    x = 5 / 0
except ZeroDivisionError as e:
    print(e)

La variable e apunta hacia una información imprimible más amplia sobre la excepción en cuestión. Podemos hacer más cosas con los objetos excepción, pero lamentablemente, eso está más allá del ámbito de este capítulo.

18.5 Generamos Una Excepción

Las excepciones pueden generarse con el comando raise. Por ejemplo:

# Generamos excepciones
def get_input():
    entrada_usuario = input("Escribe algo: ")
    if len(entrada_usuario) == 0:
        raise IOError("No has escrito nada")

get_input()

Usando el código anterior, intenta añadir una excepción que maneje el IOError levantado.

También es posible crearnos excepciones personalizadas, pero esto también está fuera del ámbito de este capítulo. Los que tengan más curiosidad, pueden acudir a:
http://docs.python.org/tutorial/errors.html#raising-exceptions

18.6 Uso Apropiado de las Excepciones

Las excepciones no deberían ser utilizadas cuando una simple declaración if podría manejar la situación. El código normal no debería lanzar excepciones durante lo que se conoce como “happy path” (”prueba feliz”?). Un código try/catch bien implementado es fácil de leer, pero un código que incluye muchas excepciones y saltos puede ser una pesadilla a la hora de depurar. (Una vez tuve la tarea de depurar un código que leía un documento XML. El código generaba docenas de excepciones para cada línea que leía del archivo. Todo era enormemente lento y sujeto a errores. Ese código no debería haber generado una sola excepción en el curso normal de leer un archivo.)

18.7 Repaso

18.7.1 Test

Haz click para ir al Test.

18.7.2 Ejercicios

Haz click para ir a los Ejercicios.


You are not logged in. Log in here and track your progress.