USM Numérica

Errores en Python

Objetivos

  1. Aprender a diagosticar y solucionar errores comunes en python.
  2. Aprender técnicas comunes de debugging.

0.1 Instrucciones

Las instrucciones de instalación y uso de un ipython notebook se encuentran en el siguiente link.

Después de descargar y abrir el presente notebook, recuerden:

  • Desarrollar los problemas de manera secuencial.
  • Guardar constantemente con Ctr-S para evitar sorpresas.
  • Reemplazar en las celdas de código donde diga FIX_ME por el código correspondiente.
  • Ejecutar cada celda de código utilizando Ctr-Enter

0.2 Licenciamiento y Configuración

Ejecutar la siguiente celda mediante Ctr-S.


In [1]:
"""
IPython Notebook v4.0 para python 3.0
Librerías adicionales: IPython, pdb
Contenido bajo licencia CC-BY 4.0. Código bajo licencia MIT. 
(c) Sebastian Flores, Christopher Cooper, Alberto Rubio, Pablo Bunout.
"""
# Configuración para recargar módulos y librerías dinámicamente
%reload_ext autoreload
%autoreload 2

# Configuración para graficos en línea
%matplotlib inline

# Configuración de estilo
from IPython.core.display import HTML
HTML(open("./style/style.css", "r").read())


Out[1]:

Contenido

  1. Introducción
  2. Técnicas de debugging.

Sobre el Notebook

Existen 4 desafíos:

  • En todos los casos, documenten lo encontrado. Escriban como un #comentario o """comentario""" los errores que vayan detectando.
  • En el desafío 1: Ejecute la celda, lea el output y arregle el código. Comente los 5 errores en la misma celda.
  • En el desafío 2: Ejecute la celda y encuentre los errores utilizando print. Comente los 3 errores en la misma celda.
  • En el desafío 3: Ejecute el archivo ./mat281_code/desafio_3.py, y encuentre los 3 errores utilizando pdb.set_trace()
  • En el desafío 4: Ejecute el archivo ./mat281_code/desafio_4.py, y encuentre los 3 errores utilizando IPython.embed()

1. Introducción

Debugging: Eliminación de errores de un programa computacional.

  • Fácilmente 40-60% del tiempo utilizado en la creación de un programa.
  • Ningún programa está excento de bugs/errores.
  • Imposible garantizar utilización 100% segura por parte del usuario.
  • Programas computaciones tienen inconsistencias/errores de implementación.
  • ¡Hardware también puede tener errores!

1. Introducción

¿Porqué se le llama bugs?

Existen registros en la correspondencia de Thomas Edisson, en 1848, hablaba de bugs para referirse a errores en sus inventos. El término se utilizaba ocasionalmente en el dominio informático. En 1947, el ordenador Mark II presentaba un error. Al buscar el origen del error, los técnicos encontraron una polilla, que se había introducido en la máquina.

Toda la historia en el siguiente enlace a wikipedia (ingles).

2. Técnicas para Debug

  1. Leer output entregado por python para posibles errores
  2. Utilizando print
  3. Utilizando pdb: python debugger
  4. Lanzamiento condicional de Ipython embed

2.1 Debug: Leer output de errores

Cuando el programa no funciona y entrega un error normalmente es fácil solucionarlo. El mensaje de error entregará: la línea donde se detecta el error y el tipo de error.

PROS:

  • Explicativo
  • Fácil de detectar y reparar

CONTRA:

  • No todos los errores arrojan error, particularmente los errores conceptuales.

2.1.1 Lista de errores comunes

Los errores más comunes en un programa son los siguientes:

  • SyntaxError:
    • Parentésis no cierran adecuadamente.
    • Faltan comillas en un string.
    • Falta dos puntos para definir un bloque if-elif-ese, una función, o un ciclo.
  • NameError:
    • Se está usando una variable que no existe (nombre mal escrito o se define después de donde es utilizada)
    • Todavía no se ha definido la función o variable.
    • No se ha importado el módulo requerido
  • IOError: El archivo a abrir no existe.
  • KeyError: La llave no existe en el diccionario.
  • TypeError: La función no puede aplicarse sobre el objeto.
  • IndentationError: Los bloques de código no están bien definidos. Revisar la indentación.

Un error clásico y que es dificil de detectar es la asignación involuntaria: Escribir $a=b$ cuando realmente se quiere testear la igualdad $a==b$.

Desafío 1

Arregle el siguiente programa en python para que funcione. Contiene 5 errores. Anote los errores como comentarios en el código.

Al ejecutar sin errores, debería regresar el valor 0.333384348536


In [3]:
import numpy as np
def promedio_positivo(a):
    pos_mean = a[a>0].mean()
    return pos_mean

N = 100
x = np.linspace(-1,1,N)
y = 0.5 - x**2   # No cambiar esta linea
print(promedio_positivo(y))

# Error 1:
# Error 2: 
# Error 3:
# Error 4:
# Error 5:


0.333384348536

2.2 Debug: Utilización de print

Utilizar print es la técnica más sencilla y habitual, apropiada si los errores son sencillos.

PRO:

  • Fácil y rápido de implementar.
  • Permite inspeccionar valores de variable a lo largo de todo un programa

CONTRA:

  • Requiere escribir expresiones más complicadas para estudiar más de una variable simultáneamente.
  • Impresión no ayuda para estudiar datos multidimensionales (arreglos, matrices, diccionarios grandes).
  • Eliminacion de múltiples print puede ser compleja en programa grande.
  • Inapropiado si la ejecución del programa tarde demasiado (por ejemplo si tiene que leer un archivo de disco), pues habitualmente se van insertando prints a lo largo de varias ejecuciones "persiguiendo" el valor de una variable.

Consejo

Si se desea inspeccionar la variable mi_variable_con_error, utilice

print("!!!" + str(mi_variable_con_error))

o bien

print(mi_variable_con_error) #!!!

De esa forma será más facil ver en el output donde está la variable impresa, y luego de solucionar el bug, será también más fácil eliminar las expresiones print que han sido insertadas para debugear (no se confundirá con los print que sí son necesarios y naturales al programa).

Desafío 2

Detecte porqué el programa se comporta de manera inadecuada, utilizando print donde parezca adecuado.

No elimine los print que usted haya introducido, sólo coméntelos con #.

Arregle el desperfecto e indique con un comentario en el código donde estaba el error.


In [4]:
def fibonacci(n):
    """
    Debe regresar la lista con los primeros n numeros de fibonacci.
    Para n<1, regresar [].
    Para n=1, regresar [1].
    Para n=2, regresar [1,1].
    Para n=3, regresar [1,1,2].
    Para n=4, regresar [1,1,2,3].
    Y sucesivamente
    """
    a = 1
    b = 1
    fib = [a,b]
    count = 2
    if n<1:
        return []
    if n=1:
        return [1]
    while count <= n:
        aux = a
        a = b
        b = aux + b
        count += 1
        fib.append(aux)
    return fib

print "fibonacci(-1):", fibonacci(-1) # Deberia ser []
print "fibonacci(0):", fibonacci(0) # Deberia ser []
print "fibonacci(1):", fibonacci(1) # Deberia ser [1]
print "fibonacci(2):", fibonacci(2) # Deberia ser [1,1]
print "fibonacci(3):", fibonacci(3) # Deberia ser [1,1,2]
print "fibonacci(5):", fibonacci(5) # Deberia ser [1,1,2,3,5]
print "fibonacci(10):", fibonacci(10) # Deberia ser ...

"""
ERRORES DETECTADOS:
1)
2)
3)
"""


  File "<ipython-input-4-09d22b1810c7>", line 17
    if n=1:
        ^
SyntaxError: invalid syntax

2.3 Debug: Utilización de pdb

Python trae un debugger por defecto: pdb (python debugger), que guarda similaridades con gdb (el debugger de C).

PRO:

  • Permite inspeccionar el estado real de la máquina en un instante dado.
  • Permite ejecutar las instrucciones siguientes.

CONTRA:

  • Requiere conocer comandos.
  • No tiene completación por tabulación como IPython.

El funcionamiento de pdb similar a los breakpoints en matlab.

Se debe realizar lo siguiente:

  1. Importar la librería

     import pdb
  2. Solicitar que se ejecute la inspección en las líneas que potencialmente tienen el error. Para ello es necesario insertar en una nueva línea, con el alineamiento adecuado, lo siguiente:

     pdb.set_trace()
  3. Ejecutar el programa como se realizaría normalmente:

     $ python mi_programa_con_error.py

Al realizar las acciones anteriores, pdb ejecuta todas las instrucciones hasta el primer pdb.set_trace() y regresa el terminal al usuario, para que inspeccione las variables y revise el código. Los comandos principales a memorizar son:

  • n + Enter: Permite ejecutar la siguiente instrucción (línea).
  • c + Enter: Permite continar la ejecución del programa, hasta el próximo pdb.set_trace() o el final del programa.
  • l + Enter: Permite ver qué línea esta actualmente en ejecución.
  • p mi_variable + Enter: Imprime la variable mi_variable.
  • Enter: Ejecuta la última accion realizada en pdb.

2.3.1 Ejemplo

Ejecute el archivo ./mat281_code/ejemplo_pdb.py y siga las instrucciones que obtendrá:

 $ python  ./mat281_code/ejemplo_pdb.py

Desafío 3

Utilice pdb para debuggear el archivo ./mat281_code/desafio_3.py.

El desafío 3 consiste en hallar 3 errores en la implementación defectuosa del método de la secante: link wikipedia

Instrucciones:

  • Después de utilizar pdb.set_trace() no borre la línea creada, solo coméntela con # para poder revisar su utilización.
  • Anote en la celda a continuación los errores que ha encontrado en el archivo ./mat281_code/desafio_3.py

In [ ]:
# Desafio 3 - Errores encontrados en ./mat281_code/desafio_3.py
"""
Se detectaron los siguientes errores:
1- FIX ME - COMENTAR AQUI
2- FIX ME - COMENTAR AQUI
3- FIX ME - COMENTAR AQUI
"""

2.4 Debug: Utilización de IPython

PRO:

  • Permite inspeccionar el estado real de la máquina en un instante dado.
  • Permite calcular cualquier expresión, de manera sencilla.
  • Permite graficar, imprimir matrices, etc.
  • Tiene completación por tabulación de IPython.
  • Tiene todo el poder de IPython (%who, %whos, etc.)

CONTRA:

  • No permite avanzar a la próxima instrucción como n+Enter en pdb.

El funcionamiento de IPython es el siguiente:

  1. Importar la librería

     import IPython
  2. Solicitar que se ejecute la inspección en las líneas que potencialmente tienen el error. Para ello es necesario insertar en una nueva línea, con el alineamiento adecuado, lo siguiente:

     IPython.embed()
  3. Ejecutar el programa como se realizaría normalmente:

     $ python mi_programa_con_error.py

Al realizar las acciones anteriores, python ejecuta todas las instrucciones hasta el primer IPython.embed() y regresa el terminal interactivo IPython al usuario en el punto seleccionado, para que inspeccione las variables y revise el código.

Para salir de IPython es necesario utilizar Ctr+d.

2.3.1 Ejemplo

Ejecute el archivo ./mat281_code/ejemplo_ipython.py y siga las instrucciones que obtendrá:

 $ python  ./mat281_code/ejemplo_ipython.py

Desafío 4

Utilice IPython para debuggear el archivo ./mat281_code/desafio_4.py.

El desafío 4 consiste en reparar una implementación defectuosa del método de bisección: link wikipedia

Instrucciones:

  • Después de utilizar IPython.embed() no borre la línea, solo coméntela con # para poder revisar su utilización.
  • Anote en la celda a continuación los errores que ha encontrado en el archivo ./mat281_code/desafio_4.py

In [ ]:
# Desafio 4 - Errores encontrados en ./mat281_code/desafio_4.py
"""
Se detectaron los siguientes errores:
1- FIX ME - COMENTAR AQUI
2- FIX ME - COMENTAR AQUI
3- FIX ME - COMENTAR AQUI
"""

Resumen

El siguiente blog tiene una imagen que resume gran parte de los errores más comunes: