Underc0de

Programación Scripting => Python => Mensaje iniciado por: Sajuuk en Mayo 21, 2015, 02:55:35 PM

Título: Manejo de Excepciones en Python
Publicado por: Sajuuk en Mayo 21, 2015, 02:55:35 PM


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:

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


Si buscas más tipos de errores, consulta la documentación oficial de Python 3.4: https://docs.python.org/3.4/library/exceptions.html (https://docs.python.org/3.4/library/exceptions.html)



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) [Seleccionar]

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...

(http://i58.tinypic.com/dwq9vd.png)

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) [Seleccionar]

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) [Seleccionar]

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) [Seleccionar]

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:

(http://i61.tinypic.com/2i71lrq.png)
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) [Seleccionar]

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) [Seleccionar]

>>> 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) [Seleccionar]

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) [Seleccionar]

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) [Seleccionar]

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) [Seleccionar]

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) [Seleccionar]

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.