Pensamiento Computacional · Unidad 5 — Entrada, salida y errores
Manejo de errores
try / except deja que el programa siga cuando algo falla. Pero atrapar todo sin mirar esconde los errores en vez de arreglarlos.
La explicación
El problema
Un error corta el programa. Si el usuario escribe "hola" donde iba un número,
int() tira ValueError y todo lo que venía después no se ejecuta.
try y except
edad = int(input("Edad: "))
except ValueError:
print("Eso no es un número")
Python intenta lo del try. Si sale bien, saltea el except. Si falla
con ese error, ejecuta el except y el programa sigue.
Los errores que más vas a ver
TypeError "a" + 1 tipos incompatibles
ZeroDivisionError 10 / 0
IndexError [1,2][5] posición que no existe
KeyError d["pepe"] clave que no existe
FileNotFoundError open("no.txt")
NameError usar una variable no definida
TypeError que menciona NoneType casi
siempre significa que una función terminó sin return — lo que vimos en el tema 2.5.
Varios except
resultado = int(a) / int(b)
except ValueError:
print("No son números")
except ZeroDivisionError:
print("No se puede dividir por cero")
Se ejecuta el primero que coincida, igual que un elif.
El except vacío, y por qué no conviene
...
except: # atrapa absolutamente todo
pass
NameError, quedaría atrapado ahí, y el programa seguiría como si nada —
dando resultados mal sin que nadie se entere. Atrapá el error que esperás, no todos.
Los dos bloques que faltan: else y finally
r = a / b
except ZeroDivisionError:
print("no se puede dividir por cero")
else:
print("salió bien") # sólo si el try NO falló
finally:
print("esto corre siempre")
El else de un try es lo contrario del else de un
if: corre cuando no hubo excepción. Sirve para dejar en el try
sólo la línea riesgosa y todo lo demás afuera.
finally corre siempre, haya fallado o no, y aunque en el medio haya un
return: Python se guarda el valor que ibas a devolver, ejecuta el
finally y recién ahí sale de la función. Por eso es el lugar donde se cierra lo que
hay que cerrar — un archivo, una conexión — cuando no estás usando with.
Validar en un ciclo
try:
edad = int(input("Edad: "))
break # sólo llega acá si el int() funcionó
except ValueError:
print("Poné un número entero")
Es el patrón estándar para pedir un dato hasta que sea válido. El break va
después de la línea que puede fallar: si falla, nunca se alcanza.
Escribir una función que pida un número por teclado y no siga hasta que el usuario escriba uno válido.
while True:
try:
return int(input("Número: "))
except ValueError:
print("Eso no es un número entero. Probá de nuevo.")
Cómo corre. Si el usuario escribe 25, el int() funciona, el
return devuelve 25 y la función termina. Si escribe hola, el
int() falla, se ejecuta el except, y el while True da
otra vuelta.
return hace de break. Está en la misma línea que puede
fallar: si int() explota, nunca se llega a devolver nada. Si hubieras escrito
return int(numero) ← el error ocurre acá
return
afuera del try.
Por qué except ValueError y no except a secas. Si el usuario
corta el programa con Ctrl+C, un except vacío lo atraparía también y no habría
forma de salir. Atrapando sólo ValueError, todo lo demás sigue funcionando normal.
Dónde entrarían else y finally. Acá no hacen falta, pero si
quisieras avisar "listo" sólo cuando la conversión salió bien, ese aviso va en un
else; y si tuvieras que cerrar algo pase lo que pase, va en un
finally — que corre incluso con ese return adentro del
try.
Control: probá con un número, con texto, y con el Enter vacío. Los tres tienen que
comportarse como esperás — el vacío también es ValueError.
Escribir una función dividir(a, b) que devuelva a/b y no se rompa nunca: si b es cero tiene que devolver un mensaje, y si alguno de los dos no es un número, otro. Explicar por qué conviene nombrar los errores en vez de usar un except suelto.
Los dos errores que puede tirar una división, y son distintos:
"a" / 2 → TypeError
La función, con un except por cada uno:
try:
return a / b
except ZeroDivisionError:
return "No se puede dividir por cero"
except TypeError:
return "Los dos tienen que ser numeros"
Los except se prueban en orden y se ejecuta el primero que coincida con el error que salió. Los demás se saltean.
Adentro del try va solo la línea que puede fallar. Si se mete código de más, un error en cualquier otra parte también quedaría atrapado y reportado como si fuera una división por cero.
Las pruebas:
dividir(-9, 3) → -3.0
dividir(1, 0) → "No se puede dividir por cero"
dividir("a", 2) → "Los dos tienen que ser numeros"
dividir(2, "a") → "Los dos tienen que ser numeros"
Ahora la segunda parte: por qué no usar un except suelto.
return a / b
except: # NO
return "Hubo un error"
Eso atrapa absolutamente todo, incluidos los errores que no tienen nada que ver:
un índice fuera de rango (IndexError)
el Ctrl+C con el que querés cortar el programa
cualquier error tuyo, adentro del try
El resultado es un programa que nunca se rompe y nunca anda. Si escribiste a / bb por error, con un except suelto el programa contesta "Hubo un error" tranquilamente, y podés pasar horas buscando un bug que el propio except está escondiendo.
La regla: atrapar solo los errores que sabés que pueden pasar y para los que tenés una respuesta. Si no sabés qué hacer con un error, es mejor que el programa se rompa y te muestre dónde.
Una variante que conviene conocer, cuando los dos errores merecen la misma respuesta:
return "Datos invalidos"
Y otra, si lo que se quiere es avisar y seguir: se puede usar except ... as e para ver el mensaje original de Python:
print("Error:", e) # Error: division by zero
return None
Cuándo devolver un mensaje y cuándo devolver None. Devolver un texto es cómodo para mostrar, pero incómodo para seguir calculando: quien llama tiene que revisar si le devolvieron un número o una frase. Si el resultado se va a usar en más cuentas, conviene None y que el que llama decida.
Dónde se pierde el punto: poner el except TypeError antes del ZeroDivisionError creyendo que el orden no importa. Acá da igual porque los dos errores no están emparentados, pero con except Exception primero, todos los demás quedan muertos: los más específicos van siempre arriba.
Dónde se cae la mayoría
- Usar except vacío y esconder errores reales, incluidos los de tipeo.
- Poner el break antes de la línea que puede fallar, y salir del ciclo con el dato inválido.
- Envolver medio programa en un try enorme: cuando falla, no se sabe qué parte fue.
Hasta acá la explicación. La práctica es la otra mitad: 8 ejercicios de este tema, en cuatro niveles, que no te dicen sólo si está bien al final sino que te corrigen en cada paso y te explican por qué. Y uno para escribir el programa vos, que se prueba ejecutándolo en Python ahí mismo, en el navegador. Además trae una animación que muestra la idea en movimiento.
Practicar Manejo de errores →Dónde entra este tema en el parcial
- Segundo parcial — try, except y finally10 puntos · resuelto paso a paso
- Segundo parcial — El promedio con datos sucios20 puntos · resuelto paso a paso
Los otros temas de la unidad 5
Entrada, salida y errores