17 de mayo de 2016

27. Propagación de errores y validaciones

Propagación de errores (raise)

Cuando se produce algún tipo de error, se puede actuar de muchas formas, según el caso. Se puede informar al usuario mediante algún mensaje, se puede mostrar información del contexto en el que se produjo, el tipo de error, etc. para que otro programador lo tenga en cuenta si tiene que modificar o corregir el programa o...
... Se puede hacer que, si la excepción se produjo dentro de la llamada a una función, el error se propague al código que realizó la llamada. Sería algo así como "pasarle el marrón" a quien lo produjo. Por ejemplo, en el siguiente código, el método raise serviría para informar de forma "personalizada" que al dividir entre cero se ha producido un error de división.
# *-* coding:utf-8 *-*

def divide(dividendo, divisor):
    try:
        resultado =  dividendo/divisor
        return resultado
        
    except ZeroDivisionError:
         raise ZeroDivisionError("El divisor no puede ser cero")    
    
divisor = int(raw_input("Divisor (escribe cero): "))
dividendo = int(raw_input("Dividendo: "))

print divide(dividendo, divisor)

= = =
Quizás quede más claro en el siguiente ejemplo...
  1. El programa principal llama a la función f
  2. En la función f se llama a la función g
  3. En g, se llama la h.
  4. En h se levanta una excepción ValueError.
  5. Esta excepción vuelve a g.
  6. Esta excepción retorna a f
  7. Y en f ya se retorna, imprimiendo el texto que se le dio en la función h.
# *-* coding:utf-8 *-*

# Función F
def f():
    try:
        g()
    except ValueError, detalle:
        print "Caught a ValueError:", detalle.message

#Función G
def g(): 
    h()

# Función H
def h():
    raise ValueError('Esta es una prueba.')

# Programa principal
f()
= = =

Validaciones

Las validaciones son técnicas de programación defensiva, que intentan comprobar sobre todo pre-condiciones en las funciones, y devolver información valiosa si no se cumplen.
Se usan par comprobar el tipo de un dato, si está dentro de un determinado rango (o dominio) o que el dato tenga una determinada característica. La validación tiene que informar convenientemente del error. En cualquier caso, lo importante es que el resultado generado por nuestro código cuando funciona correctamente y el resultado generado cuando falla debe ser claramente distinto.
Comprobaciones de dominio o rango, o de contenido
Intentamos que información externa, entregada por el usuario o recuperada de un fichero, sea adecuada, o bien porque pueda dudarse de que lo sea o no. No siempre es fácil. Por ejemplo:
# *-* coding: utf-8 *-*

def raiz(n):
    """
    pre: acepta un número mayor que cero
    post: devuelve un número que es la raíz cuadrada.
    """
    assert n>=0, "No puedo aceptar valores negativos"
    resultado = n ** (0.5)
    return resultado
    
valor = int(raw_input("Valor a calcular la raíz cuadrada: "))
print raiz(valor)
= = =
Validando datos del usuario
No podemos suponer que los usuarios introduzcan correctamente los datos. Es, pues, deseable programar para evitar circunstancias como estas
# *-* coding: utf-8 *-*

def raiz(n):
    """
    pre: acepta un número mayor que cero
    post: devuelve un número que es la raíz cuadrada.
    """
    assert n>=0, "No puedo aceptar valores negativos"
    resultado = n ** (0.5)
    return resultado
    
def leer_dato():
    """
    post: Debe ingresar un número entero. Al usuario se le 
    pedirá un número entero de forma indefinida.
    """
    while True:
        valor = raw_input("Valor a calcular la raíz cuadrada: ")
        try:
            valor = int(valor)
            return valor
        except ValueError:
            print "ATENCIÓN: Debe ingresar un número entero."

valor = leer_dato()
print raiz(valor)
Y un ejemplo en el que sólo se deja al usuario introducir el dato un número limitado de veces.
...
def leer_dato():
    """
    post: Debe ingresar un número entero. Al usuario se le 
    pedirá un número entero una cantidad de veces limitada.
    """
    t = 1
    while t<=3:
        valor = raw_input("Valor a calcular la raíz cuadrada: ")
        try:
            valor = int(valor)
            return valor
        except ValueError:
            t += 1
    raise ValueError, "No has introducido un dato numérico"
...
= = =
Comprobaciones de tipo
Para comprobar si una variable es de un tipo determinado se utiliza la función type, que devuelve el tipo de datos (entero, float, cadena, etc.) de la variable.
if type(i) != int:
    raise TypeError, "i debe ser del tipo int"Si el tipo de datos no es entero, se propaga un error.
Si el tipo de datos se quisiera comprobar que pertenece a más de un tipo puede hacerse
if type(i) not in (int, float, long, complex):
    raise TypeError, "i debe ser numérico"
O, incluso mejor
if not isinstance(i, (int, float, long, complex) ):
    raise TypeError, "i debe ser numérico"
= = =
Comprobaciones de características
No entraremos en muchos detalles. Simplemente saber que la función hasattr(variable, función) vale true si a la variable se le puede aplicar la función (por ejemplo, si variable es un número, entonces se le puede aplicar una función numérica, pero si es de cadena no).
= = =

No hay comentarios:

Publicar un comentario