Nivel: Intermedio
Tema: Excepciones, validación, mensajes útiles, fallar rápido vs. tolerancia, tests con pytest
Objetivo: Aprender a manejar errores de forma profesional: capturar lo necesario, no ocultar fallos, y devolver resultados consistentes.

Enunciado
Crea una función llamada dividir_seguro(a, b) que:
- Reciba dos valores:
ayb. - Si
aobesNone, lanceTypeError. - Si
aobno son numéricos (intofloat), lanceTypeError. - Si
bes0, no debe lanzarZeroDivisionError. En su lugar debe devolverNone. - Si
bno es0, debe devolver el resultado dea / bcomofloat. - No debe usar un
except:genérico que oculte errores inesperados.
La clave del reto: usar
try/exceptde forma mínima y específica.
Ejemplos
dividir_seguro(10, 2)→5.0dividir_seguro(3, 2)→1.5dividir_seguro(10, 0)→Nonedividir_seguro(None, 2)→ TypeErrordividir_seguro("10", 2)→ TypeError
Pistas
- Valida tipos antes de dividir.
- Puedes evitar el
try/exceptcomprobandob == 0antes. - Si usas
try/except, captura únicamenteZeroDivisionError.
Solución explicada (paso a paso)
- Rechazamos
NoneconTypeError(entrada inválida). - Validamos que
aybsean numéricos. - Si
bes 0, devolvemosNonepara evitar el error. - Si no, devolvemos
float(a / b).
Este enfoque evita try/except innecesarios y hace el comportamiento totalmente predecible.
def dividir_seguro(a, b) -> float | None: """ Divide a entre b de forma segura. Reglas: - None en a o b -> TypeError - a y b deben ser int o float -> TypeError - b == 0 -> None - caso normal -> float(a / b) """ if a is None or b is None: raise TypeError("Los parámetros 'a' y 'b' no pueden ser None.") if not isinstance(a, (int, float)) or not isinstance(b, (int, float)): raise TypeError("Los parámetros 'a' y 'b' deben ser numéricos (int o float).") if b == 0: return None return float(a / b)
import pytestfrom reto_20_dividir_seguro import dividir_segurodef test_division_entera(): assert dividir_seguro(10, 2) == 5.0def test_division_decimal(): assert dividir_seguro(3, 2) == 1.5def test_division_por_cero_devuelve_none(): assert dividir_seguro(10, 0) is Nonedef test_none_lanza_typeerror(): with pytest.raises(TypeError): dividir_seguro(None, 2) with pytest.raises(TypeError): dividir_seguro(2, None)def test_tipo_incorrecto_lanza_typeerror(): with pytest.raises(TypeError): dividir_seguro("10", 2) with pytest.raises(TypeError): dividir_seguro(10, "2")
Ejecuta:
pytest -q
Variante adicional (opcional): usando try/except bien hecho
Si quieres practicar try/except de forma explícita (sin ocultar errores):
def dividir_seguro_try(a, b) -> float | None:
if a is None or b is None:
raise TypeError("Los parámetros 'a' y 'b' no pueden ser None.")
if not isinstance(a, (int, float)) or not isinstance(b, (int, float)):
raise TypeError("Los parámetros 'a' y 'b' deben ser numéricos (int o float).")
try:
return float(a / b)
except ZeroDivisionError:
return None
Lo que aprendiste
- Cuándo evitar
try/excepty validar primero - Cómo capturar excepciones específicas sin “tragar” errores
- Diseñar funciones predecibles (contratos claros)
- Tests para comportamientos esperados y casos borde
Accede al código completo y a los tests en GitHub para ejecutar y modificar la solución localmente.
A partir de aquí, estás listo para Intermedio+.
El siguiente paso recomendado es Reto #21 — Parsear logs y extraer métricas (archivos + parsing + agregación), un caso muy real de trabajo.