Errores y excepciones

  • Syntax errors: el código no es válido (fácil de arreglar)
  • Runtime errors: código es sintácticamente válido, pero no ejecuta ()
  • Sematic errors: errores de lógica (difícil de identificar y arreglar)

Errores de "runtime"


In [1]:
print(Q)


---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-1-e796bdcf24ff> in <module>()
----> 1 print(Q)

NameError: name 'Q' is not defined

In [2]:
1 + 'abc'


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-2-aab9e8ede4f7> in <module>()
----> 1 1 + 'abc'

TypeError: unsupported operand type(s) for +: 'int' and 'str'

In [3]:
2 / 0


---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-3-ae0c5d243292> in <module>()
----> 1 2 / 0

ZeroDivisionError: division by zero

In [4]:
L = [1, 2, 3]
L[1000]


---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-4-06b6eb1b8957> in <module>()
      1 L = [1, 2, 3]
----> 2 L[1000]

IndexError: list index out of range

Pero Python es tan lindo que nos da un montón de información sobre dónde está el error.

"Catching" las excepciones: try and except

La estructura básica de la cláusula try...except es:


In [5]:
try:
    print("this gets executed first")
except:
    print("this gets executed only if there is an error")


this gets executed first

El segundo bloque de código no se ejecuta porque el primero no reportó ningún error. Pongamos un problema, entonces!


In [7]:
try:
    print("let's try something:")
    x = 1 / 0 # ZeroDivisionError
except:
    print("something bad happened!")


let's try something:
something bad happened!

Cuando algo pasa en el bloque try (una division por cero, en este caso), el error fue "caught" (atrapado) y el bloque except se ejecuta.

Una de las cosas para lo que se usa esto es para chequear "user input" o "file input", que son temas de la próxima clase. Pero nos podemos imaginar una función que sea más amistosa, y nos permita dividir por cero. Por ejemplo:


In [8]:
def safe_divide(a, b):
    try:
        return a / b
    except:
        return 1E100

In [9]:
safe_divide(1, 2)


Out[9]:
0.5

In [10]:
safe_divide(2, 0)


Out[10]:
1e+100

Pero qué pasa si la excepción es de otra clase?


In [12]:
safe_divide (1, '2') # esto no es división por cero XD


Out[12]:
1e+100

la división por un string "raises" (levanta?) una excepción llamada TypeError, pero nuestro código asume que cualquier cosa es un ZeroDivisionError, lo cual es erróneo. A veces es mejor "agarrar" (catch) las excepciones explicitamente.


In [13]:
def safe_divide(a, b):
    try:
        return a / b
    except ZeroDivisionError:
        return 1E100

In [14]:
safe_divide(1, 0)


Out[14]:
1e+100

In [15]:
safe_divide(1, '2')


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-15-2331af6a0acf> in <module>()
----> 1 safe_divide(1, '2')

<ipython-input-13-10b5f0163af8> in safe_divide(a, b)
      1 def safe_divide(a, b):
      2     try:
----> 3         return a / b
      4     except ZeroDivisionError:
      5         return 1E100

TypeError: unsupported operand type(s) for /: 'int' and 'str'

Ahora podemos entonces agarrar los errores específicos de "división por cero", y dejar pasar los otros normalmente.

Raiseing excepciones

Obviamente, es bueno tener excepciones que son informativas. Hay veces que también queremos raise nuestras propias excepciones!


In [16]:
raise RuntimeError("my error message")


---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
<ipython-input-16-c6a4c1ed2f34> in <module>()
----> 1 raise RuntimeError("my error message")

RuntimeError: my error message

For example, take our fibonacci function:


In [17]:
def fibonacci(N):
    L = []
    a, b = 0, 1
    while len(L) < N:
        a, b = b, a + b
        L.append(a)
    return L

Pero N puede ser negativo! Errores que se originan en valores pasados por argumentos, se llaman ValueErrors.


In [18]:
def fibonacci(N):
    if N < 0:
        raise ValueError("N must be non-negative")
    L = []
    a, b = 0, 1
    while len(L) < N:
        a, b = b, a + b
        L.append(a)
    return L

In [19]:
fibonacci(10)


Out[19]:
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

In [20]:
fibonacci(-10)


---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-20-3d291499cfa7> in <module>()
----> 1 fibonacci(-10)

<ipython-input-18-01d0cf168d63> in fibonacci(N)
      1 def fibonacci(N):
      2     if N < 0:
----> 3         raise ValueError("N must be non-negative")
      4     L = []
      5     a, b = 0, 1

ValueError: N must be non-negative

Ahora el usuario sabe que el "input" es inválido. Y hasta podemos usar un try...except


In [21]:
N = -10
try:
    print("trying this...")
    print(fibonacci(N))
except ValueError:
    print("Bad value: need to do something else")


trying this...
Bad value: need to do something else

try...except...else...finally

Hay veces que podemos ser más expresivos todavía en el manejo de flujo.


In [22]:
try:
    print("try something here")
except:
    print("this happens only if it fails")
else:
    print("this happens only if it succeeds")
finally:
    print("this happens no matter what")


try something here
this happens only if it succeeds
this happens no matter what

finally es como un estadio de limpieza del código (cerrar archivos, volver variables a 0, meh...)