excepciones en Python ejercicios resueltos chuletario

Excepciones en Python — ejercicios para escribir código que no se rompe

Las excepciones en Python ejercicios con solución cierran este bloque. Ya viste la teoría y practicaste con programas reales. Ahora toca resolver por tu cuenta. Tres niveles de dificultad con especial atención al error más común de FP2, capturar Exception genérico en vez de excepciones específicas, ocultando errores reales en el código. Corrige los errores, revisando el flujo del código en pythontutor.com.


Excepciones en Python ejercicios — Nivel Básico

Ejercicio 1 — Conversor de unidades que gestiona entradas incorrectas

Escribe un conversor de unidades que permita convertir entre sistemas métrico e imperial. El programa debe gestionar todas las entradas incorrectas con excepciones específicas y nunca romperse.

Conversiones:

  • Kilómetros → Millas: multiplicar por 0.621371
  • Millas → Kilómetros: multiplicar por 1.60934
  • Kilogramos → Libras: multiplicar por 2.20462
  • Libras → Kilogramos: dividir entre 2.20462
  • Celsius → Fahrenheit: multiplicar por 9/5 y sumar 32
  • Fahrenheit → Celsius: restar 32 y multiplicar por 5/9

La salida debe ser:

=== CONVERSOR DE UNIDADES ===
1. km → millas
2. millas → km
3. kg → libras
4. libras → kg
5. Celsius → Fahrenheit
6. Fahrenheit → Celsius
0. Salir

Elige conversión: abc
Error: introduce un número entero entre 0 y 6

Elige conversión: 1
Valor a convertir: -5
Error: el valor no puede ser negativo para esta conversión

Elige conversión: 1
Valor a convertir: hola
Error: introduce un número válido

Elige conversión: 1
Valor a convertir: 100
100.0 km = 62.14 millas

💡 Pista — el error del Exception genérico:

# MAL — capturas todo y no sabes qué falló
try:
    opcion = int(input('Elige: '))
    valor = float(input('Valor: '))
except Exception:
    print('Algo salió mal')    # ¿qué salió mal exactamente?

# BIEN — capturas lo específico
try:
    opcion = int(input('Elige: '))
except ValueError:
    print('Error: introduce un número entero')
    continue

try:
    valor = float(input('Valor: '))
except ValueError:
    print('Error: introduce un número válido')
    continue

Capturar Exception genérico parece más cómodo pero oculta qué error ocurrió, y en el examen de FP2 los profesores lo detectan inmediatamente.


Excepciones en Python ejercicios — Nivel Intermedio

Ejercicio 2 — Validador de contraseñas con excepciones propias

Diseña un sistema de validación de contraseñas con su propia jerarquía de excepciones. La contraseña debe cumplir estas reglas:

  • Longitud mínima de 8 caracteres
  • Al menos una letra mayúscula
  • Al menos un dígito
  • Al menos un carácter especial (!@#$%^&*)
  • No puede contener espacios

Define estas excepciones propias:

ErrorContrasena (base)
├── ErrorLongitud      — menos de 8 caracteres
├── ErrorMayuscula     — sin mayúsculas
├── ErrorDigito        — sin dígitos
├── ErrorEspecial      — sin caracteres especiales
└── ErrorEspacio       — contiene espacios

La salida debe ser:

=== VALIDADOR DE CONTRASEÑA ===

Contraseña: abc
Error de longitud: mínimo 8 caracteres (tiene 3)

Contraseña: abcdefgh
Error de mayúscula: debe contener al menos una mayúscula

Contraseña: Abcdefgh
Error de dígito: debe contener al menos un número

Contraseña: Abcdef1h
Error especial: debe contener al menos un carácter especial (!@#$%^&*)

Contraseña: Abcdef1!
✓ Contraseña válida

💡 Pistas:

  • Define primero la clase base ErrorContrasena(Exception) y luego las subclases
  • Crea una función validar_contrasena(contrasena) que lanza la excepción correspondiente cuando detecta el primer error
  • Llama a la función dentro de un try y captura cada excepción por separado con mensajes específicos
  • El error del Exception genérico es especialmente peligroso aquí — si capturas Exception en vez de ErrorLongitud, ErrorMayuscula… etc, no podrás dar mensajes de error distintos para cada caso

Excepciones en Python ejercicios — Desafío Final

Ejercicio 3 — Agenda con lectura y escritura de ficheros

Implementa una agenda de contactos que guarda y carga datos de un fichero de texto. Cada contacto tiene nombre y teléfono, separados por coma en el fichero.

El fichero agenda.txt tiene este formato:

Sergio,612345678
Sofía,698765432
Juan,654321987

El programa debe:

  1. Cargar la agenda del fichero al iniciar — si no existe, empezar con agenda vacía
  2. Mostrar un menú: ver contactos, añadir contacto, buscar contacto, guardar y salir
  3. Gestionar todos los posibles errores con excepciones específicas
  4. Guardar siempre en el finally al salir — aunque haya ocurrido un error
=== AGENDA DE CONTACTOS ===
Cargando agenda...
Agenda cargada: 3 contactos

1. Ver todos
2. Añadir contacto
3. Buscar contacto
0. Guardar y salir

Opción: 2
Nombre: Ana
Teléfono: 123abc
Error: el teléfono solo puede contener dígitos

Teléfono: 612111222
Contacto añadido: Ana — 612111222

Opción: 0
Agenda guardada: 4 contactos
Hasta luego

💡 Pistas:

  • Crea una excepción propia ErrorTelefono para teléfonos inválidos
  • Para cargar el fichero usa FileNotFoundError, si no existe, crea una agenda vacía sin avisar de error
  • Para guardar usa finally, así el fichero se guarda aunque el usuario interrumpa el programa con Ctrl+C (que lanza KeyboardInterrupt)
  • El error del Exception genérico es crítico en la lectura del fichero — si una línea está mal formada (sin coma, campo vacío) debes ignorarla con un mensaje, no romper toda la carga
  • Para comprobar que el teléfono solo tiene dígitos usa el método .isdigit() de las cadenas

Soluciones Comentadas

Solución Ejercicio 1:

CONVERSIONES = {
    1: ('km', 'millas', lambda x: x * 0.621371),
    2: ('millas', 'km', lambda x: x * 1.60934),
    3: ('kg', 'libras', lambda x: x * 2.20462),
    4: ('libras', 'kg', lambda x: x / 2.20462),
    5: ('Celsius', 'Fahrenheit', lambda x: x * 9/5 + 32),
    6: ('Fahrenheit', 'Celsius', lambda x: (x - 32) * 5/9),
}

NO_NEGATIVAS = {1, 2, 3, 4}

print('=== CONVERSOR DE UNIDADES ===')
for k, (origen, destino, _) in CONVERSIONES.items():
    print(f'{k}. {origen} → {destino}')
print('0. Salir\n')

while True:
    try:
        opcion = int(input('Elige conversión: '))
    except ValueError:
        print('Error: introduce un número entero entre 0 y 6\n')
        continue

    if opcion == 0:
        print('Hasta luego')
        break

    if opcion not in CONVERSIONES:
        print('Error: opción no válida\n')
        continue

    try:
        valor = float(input('Valor a convertir: '))
    except ValueError:
        print('Error: introduce un número válido\n')
        continue

    if opcion in NO_NEGATIVAS and valor < 0:
        print('Error: el valor no puede ser negativo para esta conversión\n')
        continue

    origen, destino, formula = CONVERSIONES[opcion]
    resultado = formula(valor)
    print(f'{valor} {origen} = {resultado:.2f} {destino}\n')

Solución Ejercicio 2:

# Jerarquía de excepciones
class ErrorContrasena(Exception):
    pass

class ErrorLongitud(ErrorContrasena):
    pass

class ErrorMayuscula(ErrorContrasena):
    pass

class ErrorDigito(ErrorContrasena):
    pass

class ErrorEspecial(ErrorContrasena):
    pass

class ErrorEspacio(ErrorContrasena):
    pass

CARACTERES_ESPECIALES = '!@#$%^&*'

def validar_contrasena(contrasena):
    if len(contrasena) < 8:
        raise ErrorLongitud(
            f'mínimo 8 caracteres (tiene {len(contrasena)})')
    if not any(c.isupper() for c in contrasena):
        raise ErrorMayuscula('debe contener al menos una mayúscula')
    if not any(c.isdigit() for c in contrasena):
        raise ErrorDigito('debe contener al menos un número')
    if not any(c in CARACTERES_ESPECIALES for c in contrasena):
        raise ErrorEspecial(
            f'debe contener al menos un carácter especial ({CARACTERES_ESPECIALES})')
    if ' ' in contrasena:
        raise ErrorEspacio('no puede contener espacios')

print('=== VALIDADOR DE CONTRASEÑA ===\n')

while True:
    contrasena = input('Contraseña: ')

    try:
        validar_contrasena(contrasena)
        print('✓ Contraseña válida')
        break
    except ErrorLongitud as err:
        print(f'Error de longitud: {err}')
    except ErrorMayuscula as err:
        print(f'Error de mayúscula: {err}')
    except ErrorDigito as err:
        print(f'Error de dígito: {err}')
    except ErrorEspecial as err:
        print(f'Error especial: {err}')
    except ErrorEspacio as err:
        print(f'Error de espacio: {err}')
    print()

Solución Ejercicio 3:

class ErrorTelefono(Exception):
    pass

def cargar_agenda(nombre_fichero):
    agenda = {}
    fichero = None
    try:
        fichero = open(nombre_fichero, 'r', encoding='utf-8')
        for numero_linea, linea in enumerate(fichero, 1):
            linea = linea.strip()
            if not linea:
                continue
            try:
                partes = linea.split(',')
                if len(partes) != 2:
                    raise ValueError('formato incorrecto')
                nombre, telefono = partes[0].strip(), partes[1].strip()
                if not nombre or not telefono:
                    raise ValueError('nombre o teléfono vacío')
                agenda[nombre] = telefono
            except ValueError as err:
                print(f'Línea {numero_linea} ignorada: {err}')
    except FileNotFoundError:
        pass    # agenda vacía — no es un error
    finally:
        if fichero is not None:
            fichero.close()
    return agenda

def guardar_agenda(agenda, nombre_fichero):
    fichero = None
    try:
        fichero = open(nombre_fichero, 'w', encoding='utf-8')
        for nombre, telefono in agenda.items():
            fichero.write(f'{nombre},{telefono}\n')
        print(f'Agenda guardada: {len(agenda)} contactos')
    except PermissionError:
        print('Error: sin permisos para guardar el fichero')
    finally:
        if fichero is not None:
            fichero.close()

def validar_telefono(telefono):
    if not telefono.isdigit():
        raise ErrorTelefono('el teléfono solo puede contener dígitos')
    if len(telefono) < 9:
        raise ErrorTelefono('el teléfono debe tener al menos 9 dígitos')

FICHERO = 'agenda.txt'
print('=== AGENDA DE CONTACTOS ===')
print('Cargando agenda...')
agenda = cargar_agenda(FICHERO)
print(f'Agenda cargada: {len(agenda)} contactos\n')

try:
    while True:
        print('1. Ver todos')
        print('2. Añadir contacto')
        print('3. Buscar contacto')
        print('0. Guardar y salir\n')

        try:
            opcion = int(input('Opción: '))
        except ValueError:
            print('Error: introduce un número\n')
            continue

        if opcion == 0:
            break
        elif opcion == 1:
            if not agenda:
                print('Agenda vacía\n')
            else:
                for nombre, tel in agenda.items():
                    print(f'{nombre}: {tel}')
                print()
        elif opcion == 2:
            nombre = input('Nombre: ').strip()
            if not nombre:
                print('Error: el nombre no puede estar vacío\n')
                continue
            while True:
                try:
                    telefono = input('Teléfono: ').strip()
                    validar_telefono(telefono)
                    agenda[nombre] = telefono
                    print(f'Contacto añadido: {nombre} — {telefono}\n')
                    break
                except ErrorTelefono as err:
                    print(f'Error: {err}')
        elif opcion == 3:
            buscar = input('Nombre a buscar: ').strip()
            if buscar in agenda:
                print(f'{buscar}: {agenda[buscar]}\n')
            else:
                print(f'"{buscar}" no encontrado en la agenda\n')
        else:
            print('Opción no válida\n')

except KeyboardInterrupt:
    print('\nInterrupción detectada')

finally:
    guardar_agenda(agenda, FICHERO)
    print('Hasta luego')

Chuletario — Excepciones en Python

# ============================================
# CHULETARIO — Excepciones en Python
# Sergio Learns · sergiolearns.com
# ============================================

# ESTRUCTURA COMPLETA
try:
    # código que puede fallar
except ValueError:
    # gestión específica de ValueError
except (TypeError, KeyError):
    # varios tipos juntos
except Exception as err:
    # cualquier excepción + acceso a info
    print(type(err), err, err.args)
except:
    # cualquier excepción sin info (evitar)
else:
    # solo si NO hubo excepción en try
finally:
    # SIEMPRE se ejecuta — cerrar recursos

# RAISE — lanzar excepciones
raise ValueError('mensaje descriptivo')
raise TypeError('tipo incorrecto')
raise    # propagar excepción actual (dentro de except)

# EXCEPCIONES PROPIAS
class MiError(Exception):
    pass

class MiErrorEspecifico(MiError):
    pass

raise MiError('algo salió mal')
raise MiErrorEspecifico('algo más específico')

# JERARQUÍA — capturar por nivel
try:
    ...
except MiErrorEspecifico:    # primero el más específico
    ...
except MiError:              # luego el más general
    ...

# ASSERT
assert condicion, 'mensaje'
# → AssertionError si condicion es False
# Para errores de programación, no de usuario

# FICHEROS CON finally
fichero = None
try:
    fichero = open('datos.txt', 'r')
    # ... usar fichero ...
except FileNotFoundError:
    print('Fichero no encontrado')
except PermissionError:
    print('Sin permisos')
finally:
    if fichero is not None:
        fichero.close()    # siempre se cierra

# EXCEPCIONES COMUNES
# ValueError        → valor incorrecto (int('hola'))
# ZeroDivisionError → división entre cero
# TypeError         → tipo incorrecto
# IndexError        → índice fuera de rango
# KeyError          → clave no encontrada en dict
# FileNotFoundError → fichero no existe
# PermissionError   → sin permisos
# AttributeError    → atributo no existe
# KeyboardInterrupt → Ctrl+C del usuario

# try/except vs if/else
# if/else  → prevenir errores conocidos y comprobables
# try/except → gestionar errores inesperados o en recursos

# ERRORES TÍPICOS
# 1. except Exception genérico → oculta qué falló realmente
# 2. Olvidar finally para cerrar ficheros
# 3. raise sin estar en un except → RuntimeError
# 4. assert para validar entrada de usuario → desactivable con -O
# 5. except específico después de genérico → nunca se ejecuta

Python exceptions — exercises to write code that never breaks

Basic Level

Exercise 1 — Unit converter with error handling

Write a unit converter for metric/imperial conversions. Handle all wrong inputs with specific exceptions, never crash.

Conversions: km↔miles, kg↔pounds, Celsius↔Fahrenheit.

💡 Hint — the generic Exception mistake:

# WRONG — catches everything, can't tell what failed
try:
    option = int(input('Choose: '))
    value = float(input('Value: '))
except Exception:
    print('Something went wrong')

# RIGHT — catch specifically
try:
    option = int(input('Choose: '))
except ValueError:
    print('Error: enter an integer')
    continue

Catching generic Exception seems easier but hides what actually failed — professors spot it immediately in FP2 exams.

Intermediate Level

Exercise 2 — Password validator with custom exceptions

Design a password validation system with its own exception hierarchy. Password rules: minimum 8 characters, at least one uppercase, one digit, one special character (!@#$%^&*), no spaces.

Define:

PasswordError (base)
├── LengthError
├── UppercaseError
├── DigitError
├── SpecialCharError
└── SpaceError

💡 Hints: Define base class first, then subclasses. Create validate_password(password) that raises the appropriate exception on first error. The generic Exception mistake is especially dangerous here, catching Exception instead of specific errors means you can’t give different messages for each case.

Final Challenge

Exercise 3 — Contact book with file reading and writing

Implement a contact book that saves and loads from a text file. Format: name,phone per line.

Features: load on start (empty if file doesn’t exist), menu to view/add/search contacts, save with finally on exit, even on Ctrl+C.

💡 Hints: Create PhoneError for invalid phones. Use FileNotFoundError silently for missing file. Use finally for saving, catches KeyboardInterrupt too. The generic Exception mistake in file reading means a badly formatted line breaks the entire load, catch ValueError per line and skip it instead.

(Solutions same as Spanish version above)

Cheat sheet — Python Exceptions

# ============================================
# CHEAT SHEET — Python Exceptions
# Sergio Learns · sergiolearns.com
# ============================================

# FULL STRUCTURE
try:
    pass
except ValueError:
    pass
except (TypeError, KeyError):
    pass
except Exception as err:
    print(type(err), err)
except:
    pass    # avoid — no info
else:
    pass    # only if no exception
finally:
    pass    # ALWAYS runs

# RAISE
raise ValueError('message')
raise    # propagate current exception

# CUSTOM EXCEPTIONS
class MyError(Exception):
    pass
class MySpecificError(MyError):
    pass

# HIERARCHY — most specific first
try: ...
except MySpecificError: ...
except MyError: ...

# ASSERT
assert condition, 'message'    # AssertionError if False
# For programming errors only — not user input

# FILES WITH finally
file = None
try:
    file = open('data.txt', 'r')
except FileNotFoundError:
    print('Not found')
finally:
    if file is not None:
        file.close()    # always closes

# COMMON EXCEPTIONS
# ValueError        → wrong value (int('hello'))
# ZeroDivisionError → divide by zero
# TypeError         → wrong type
# IndexError        → index out of range
# KeyError          → key not in dict
# FileNotFoundError → file doesn't exist
# PermissionError   → no permissions
# KeyboardInterrupt → Ctrl+C

# try/except vs if/else
# if/else    → prevent known checkable errors
# try/except → handle unexpected or resource errors

# COMMON MISTAKES
# 1. Generic Exception → hides what actually failed
# 2. Forgetting finally to close files
# 3. raise outside except → RuntimeError
# 4. assert for user input → disabled with -O
# 5. Specific except after generic → never reached

Publicaciones Similares

Un comentario

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *