Manejo de Excepciones en Python

Iniciado por Sajuuk, Mayo 21, 2015, 02:55:35 PM

Tema anterior - Siguiente tema

0 Miembros y 1 Visitante están viendo este tema.

Mayo 21, 2015, 02:55:35 PM Ultima modificación: Mayo 22, 2015, 09:33:43 PM por Barlan


Manejo de Excepciones en Python



Citar
Las excepciones son errores detectados por Python durante la ejecución del programa. Cuando el intérprete se encuentra con una situación excepcional, como el intentar dividir un número entre 0 o el intentar acceder a un archivo que no existe, este genera o lanza una excepción, informando al usuario de que existe algún problema.

En Python, contamos con 3 palabras reservadas (y una palabra opcional) para el manejo de excepciones:

  • try
  • except
  • finally

  • else (solo en algunos casos)

Además, contamos con tipos de errores definidos por Python.
Te describiré los principales (o los más comunes):


  • ImportError: Ocurre cuando Python no encuentra el modulo que intentamos importar.
  • IndexError: Cuando nosotros buscamos algo en una tupla o diccionario más alla de lo que realmente posee (ejemplo: Leer el valor #5 de una tupla que contiene solo 3 cosas).
  • KeyError: Cuando no se encuentra una clave en un Diccionario.
  • KeyboardInterrupt: Ocurre cuando nosotros presionamos Ctrl + C. Generalmente presionamos esas teclas cuando caemos en un bucle infinito y queremos pararlo sin cerrar la consola.
  • NameError: Ocurre cuando usamos una variable que no existe.
  • SyntaxError: Se muestra cuando escribimos mál alguna linea de código. Ejemplo: Usar print sin los paréntesis (Python 3.4)
  • TypeError: Ocurre cuando usamos un método en un tipo de variable que no lo posee. Ejemplo: Buscar una clave en una tupla (Usamos un método de un diccionario en una tupla).
  • ValueError: Valor del argumento no apropiado (cuando le designamos un valor a un tipo de variable que no es para eso).

Si buscas más tipos de errores, consulta la documentación oficial de Python 3.4: No tienes permitido ver los links. Registrarse o Entrar a mi cuenta



Bloque Try-Except



En try, nosotros colocaremos el código que puede llegar a un error.
En except, "atraparemos" el error lanzado por el interprete para evitar que el programa se detenga.

Código: python

try:
    print(10 / 0)
except:
    print("No se puede dividir entre cero!")


Lo anterior trataría de dividir 10 entre 0 (lo cual es imposible).
Si nosotros no escribimos el bloque except... ocurre lo siguiente:

CitarTraceback (most recent call last):
  File "C:\Users\Barlan\Desktop\ejemplos.py", line 1, in <module>
    print(10 / 0)
ZeroDivisionError: division by zero

Nos lanza un error tipo ZeroDivisionError (Error de división entre cero), lo cual nos indica que no se puede dividir un numero entre 0.

Pero si nosotros escribimos el bloque except...



Como podemos ver, el programa atrapa el error, y evita que se interrumpa a la fuerza el programa a causa de ello.




Podemos también esperar un tipo de error (de los mencionados al principio):

Código: python

try:
    x = open("Hola.txt", "r")
    print(x)
except (IOError):
    print("No se encontró el archivo.")

print("Ja! Seguí ejecutandome ;)")


Si el archivo "Hola.txt" no existe en el directorio donde se encuentra el archivo .py, nos lanzará un error tipo IOError.
Para evitar que se cierre el programa, atrapamos ese error con except, e imprimimos que no se encontró el archivo.

Y como el programa no se cerró, se imprimirá "Ja! Segui ejecutandome ;) ")




También podemos atrapar varios tipos de errores:

Código: python

try:
    x = open("Hola.txt", "r")
    y = x.readlines()
    print(y[5])
except (IOError, IndexError):  # Posiblemente ocurra alguno de estos 2 errores)
    print("Ocurrio un problema.")

print("Ja! Seguí ejecutandome ;)")





Uso de Else en Excepciones



Usamos else cuando en el bloque try, no se produjo ningún error.
Además, usar else evita que añadamos más lineas de código al try, evitando así más posibles errores.

Código: python

import sys

for arg in sys.argv[1:]:
    try:
        f = open(arg, 'r')
    except IOError:
        print('no pude abrir', arg)
    else:
        print(arg, 'tiene', len(f.readlines()), 'lineas')
        f.close()


El programa anterior importa un modulo: sys.
Este modulo tiene un método de leer argumentos al momento de ejecutar nuestro programa (argv)
Después del nombre del programa, nosotros escribimos el nombre del archivo de texto a leer.

El programa tratará de calcular cuantas lineas tiene el archivo.
Si el programa no encuentra el archivo, nos lanza un error:

Citarno pude abrir [nombre del archivo de texto]

Pero en caso de que si pudimos leer el contenido y calcular las lineas, else se encargará de notificarlo:


CitarHola.txt tiene 5 lineas




El bloque finally



El bloque de instrucciones finally se ejecutará siempre, sin importar si ocurrió algún error o no.

Código: python

try:
    print(x / 10)
except (NameError):
    print("Error. No existe la variable 'x'")
else:
    print("Soy el else.")
finally:
    print("Yo me ejecuto porque se me pega la gana! Thug Life 8)")


Generalmente se usa para labores de limpieza (como limpiar la pantalla, o pasar a otra función... etc).




raise



raise nos permite crear nuestras propias excepciones (usando como base las pre-establecidas por Python).
Ejemplo:

Código: python

>>> raise NameError('Hola')
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
NameError: Hola


Aquí forzamos a que ocurriera una Excepción tipo NameError, seguido de un mensaje personalizado.

Otro ejemplo:

Código: python

try:
    raise NameError("Hola")
except NameError:
    print("Voló una excepción!")
    raise


Vemos que raise está solo. Esto significa que hubo una excepción, pero que no quisimos manejarla. Por lo que print() hará el resto.




Excepciones desde clases



CitarLos programas pueden nombrar sus propias excepciones creando una nueva clase excepción. Las excepciones, típicamente, deberán derivar de la clase Exception, directa o indirectamente. Por ejemplo:

Código: python

class MiError(Exception):
    def __init__(self, valor):
        self.valor = valor

    def __str__(self):
        return repr(self.valor)

try:
    raise MiError(2*2)
except Exception as e:
    print("Ocurrió una excepción. Valor:", e)

raise MiError("Oops! Un error!")


Primero, llegamos al try/except:
Tratamos de calcular 2*2.
Entonces generamos una excepción.

CitarOcurrió una excepción. Valor: 4

Luego, forzamos a la clase a que nos genera una excepción con el mensaje "Oops! Un error!"




CitarUna cláusula finally siempre es ejecutada antes de salir de la declaración try, ya sea que una excepción haya ocurrido o no. Cuando ocurre una excepción en la cláusula try y no fue manejada por una cláusula except (o ocurrió en una cláusula except o else), es relanzada luego de que se ejecuta la cláusula finally.

Código: python

def dividir(x, y):
    try:
        result = x / y
    except ZeroDivisionError:
        print("¡División por cero!")
    else:
        print("El resultado es: ", result)
    finally:
        print("Instrucción finally.")

dividir(2,1)
dividir(2,0)
dividir("hi", "sudo")


CitarEl resultado es:  2.0
Instrucción finally.
¡División por cero!
Instrucción finally.

Traceback (most recent call last):
  File "ejemplos.py", line 13, in <module>
    dividir("hi", "sudo")
  File "ejemplos.py", line 3, in dividir
    result = x / y
TypeError: unsupported operand type(s) for /: 'str' and 'str'

Como podemos ver, no manejamos el error TypeError en el exception, por lo que el programa se interrumpe, y procede a imprimir el mensaje de error.




La "acción de limpieza"



Llegará un momento en el que el objeto creado ya no es requerido en más lineas de código. Por ejemplo, al leer un archivo y guardar su contenido en una variable, ya no necesitaremos tener el archivo abierto (esto puede provocar errores):

Código: python

for linea in open("miarchivo.txt"):
    print(linea, end="")


CitarEl problema con este código es que deja el archivo abierto por un periodo de tiempo indeterminado luego de que esta parte termine de ejecutarse. Esto no es un problema en scripts simples, pero puede ser un problema en aplicaciones más grandes. La declaración with permite que objetos como archivos sean usados de una forma que asegure que siempre se los libera rápido y en forma correcta:

Código: python

with open("miarchivo.txt") as f:
    for linea in f:
        print(linea, end="")


La instrucción with nos permite hacer algo solo durante algunas lineas de código. Cuando esa linea termine su trabajo, automáticamente el archivo de texto se cerrará, evitando posibles errores futuros.





Errores, sugerencias y críticas abajo en los comentarios.
Te ha hablado Barlan y te deseo... buenas noches.