Portada de libro electrónico gratuitaPython desde cero con mini-retos: aprende programando (sin teoría eterna)

Python desde cero con mini-retos: aprende programando (sin teoría eterna)

Nuevo curso

12 páginas

Mini-reto: agenda simple con operaciones CRUD usando listas

Capítulo 9

Tiempo estimado de lectura: 7 minutos

+ Ejercicio

Objetivo del mini-reto

Vas a construir una agenda de contactos por consola que permita hacer operaciones CRUD usando listas: Crear, Revisar (leer), Update (actualizar) y Delete (eliminar). La idea es practicar cómo modelar datos sencillos y cómo recorrer/editar una colección sin usar bases de datos.

Modelo de datos (simple y práctico)

Usaremos una lista llamada agenda. Cada contacto será otra lista con tres campos en orden fijo: [nombre, telefono, email]. Ejemplo: ["Ana", "555123", "ana@mail.com"]. Este formato es fácil de manipular con índices y suficiente para el reto.

Convención de índices

  • contacto[0] → nombre
  • contacto[1] → teléfono
  • contacto[2] → email

Paso a paso: construye la agenda CRUD

Paso 1: estructura base y menú

Empieza con una lista vacía y un bucle principal que muestre opciones. Mantén el menú corto y claro.

agenda = []  # cada contacto: [nombre, telefono, email]

while True:
    print("\n=== AGENDA SIMPLE ===")
    print("1) Crear contacto")
    print("2) Ver contactos")
    print("3) Actualizar contacto")
    print("4) Eliminar contacto")
    print("5) Salir")

    opcion = input("Elige una opción: ").strip()

    if opcion == "5":
        break

    # aquí irán las opciones 1-4

Paso 2: Crear (Create)

Crear significa pedir datos y agregarlos a la lista. Para evitar duplicados, puedes comprobar si ya existe un contacto con el mismo nombre (criterio simple).

def buscar_indice_por_nombre(agenda, nombre):
    nombre = nombre.strip().lower()
    for i, contacto in enumerate(agenda):
        if contacto[0].strip().lower() == nombre:
            return i
    return -1

# Dentro del while, en la opción 1:
if opcion == "1":
    nombre = input("Nombre: ").strip()
    telefono = input("Teléfono: ").strip()
    email = input("Email: ").strip()

    if nombre == "":
        print("Nombre obligatorio.")
    else:
        idx = buscar_indice_por_nombre(agenda, nombre)
        if idx != -1:
            print("Ya existe un contacto con ese nombre.")
        else:
            agenda.append([nombre, telefono, email])
            print("Contacto creado.")

Paso 3: Leer (Read) con formato

Leer es mostrar la lista. Un detalle útil es imprimir con numeración para que el usuario pueda elegir un contacto por índice en las operaciones de actualizar/eliminar.

Continúa en nuestra aplicación.

Podrás escuchar el audiolibro con la pantalla apagada, recibir un certificado gratuito para este curso y además tener acceso a otros 5.000 cursos online gratuitos.

O continúa leyendo más abajo...
Download App

Descargar la aplicación

# Dentro del while, en la opción 2:
if opcion == "2":
    if len(agenda) == 0:
        print("Agenda vacía.")
    else:
        print("\n--- CONTACTOS ---")
        for i, contacto in enumerate(agenda, start=1):
            nombre, telefono, email = contacto
            print(f"{i}. {nombre} | {telefono} | {email}")

Paso 4: Actualizar (Update) por nombre o por número

Actualizar consiste en localizar un contacto y reemplazar uno o más campos. Para hacerlo cómodo, permite dejar un campo vacío para conservar el valor anterior.

def actualizar_contacto(agenda, idx):
    nombre_actual, tel_actual, email_actual = agenda[idx]

    print("Deja en blanco para mantener el valor actual.")
    nuevo_nombre = input(f"Nombre ({nombre_actual}): ").strip()
    nuevo_tel = input(f"Teléfono ({tel_actual}): ").strip()
    nuevo_email = input(f"Email ({email_actual}): ").strip()

    if nuevo_nombre != "":
        agenda[idx][0] = nuevo_nombre
    if nuevo_tel != "":
        agenda[idx][1] = nuevo_tel
    if nuevo_email != "":
        agenda[idx][2] = nuevo_email

# Dentro del while, en la opción 3:
if opcion == "3":
    if len(agenda) == 0:
        print("No hay contactos para actualizar.")
    else:
        modo = input("Actualizar por (n)úmero o por (nombre): ").strip().lower()
        if modo == "n":
            n = input("Número de contacto: ").strip()
            if not n.isdigit():
                print("Debes escribir un número.")
            else:
                pos = int(n) - 1
                if pos < 0 or pos >= len(agenda):
                    print("Número fuera de rango.")
                else:
                    actualizar_contacto(agenda, pos)
                    print("Contacto actualizado.")
        else:
            nombre = input("Nombre a actualizar: ").strip()
            idx = buscar_indice_por_nombre(agenda, nombre)
            if idx == -1:
                print("No encontrado.")
            else:
                actualizar_contacto(agenda, idx)
                print("Contacto actualizado.")

Paso 5: Eliminar (Delete) con confirmación

Eliminar es quitar un elemento de la lista. Añade confirmación para evitar borrados accidentales.

# Dentro del while, en la opción 4:
if opcion == "4":
    if len(agenda) == 0:
        print("No hay contactos para eliminar.")
    else:
        nombre = input("Nombre a eliminar: ").strip()
        idx = buscar_indice_por_nombre(agenda, nombre)
        if idx == -1:
            print("No encontrado.")
        else:
            contacto = agenda[idx]
            confirm = input(f"¿Eliminar a {contacto[0]}? (s/n): ").strip().lower()
            if confirm == "s":
                agenda.pop(idx)
                print("Contacto eliminado.")
            else:
                print("Operación cancelada.")

Checklist de mini-reto (para comprobar que lo lograste)

  • La agenda inicia vacía y no se rompe si intentas ver/actualizar/eliminar sin contactos.
  • Crear evita duplicados por nombre (o al menos avisa).
  • Ver muestra contactos numerados y con campos separados.
  • Actualizar permite mantener valores dejando el input vacío.
  • Eliminar pide confirmación antes de borrar.

Extensiones opcionales (sube el nivel sin cambiar de herramienta)

1) Búsqueda parcial

Permite buscar por fragmento de nombre: si el usuario escribe an, mostrar Ana, Andrés, etc. Pista: compara con in usando minúsculas.

def buscar_por_fragmento(agenda, fragmento):
    fragmento = fragmento.strip().lower()
    resultados = []
    for i, contacto in enumerate(agenda):
        if fragmento in contacto[0].strip().lower():
            resultados.append(i)
    return resultados

2) Ordenar por nombre

Antes de mostrar, ordena temporalmente por nombre. Pista: usa sorted con una clave que tome contacto[0].

agenda_ordenada = sorted(agenda, key=lambda c: c[0].strip().lower())
for i, contacto in enumerate(agenda_ordenada, start=1):
    print(i, contacto[0], contacto[1], contacto[2])

3) Validación mínima de email y teléfono

Sin complicarte: email debe contener @ y teléfono debe ser solo dígitos (o permitir + al inicio). Si no pasa, pide de nuevo.

def es_email_simple(email):
    email = email.strip()
    return ("@" in email) and ("." in email)

def es_tel_simple(tel):
    tel = tel.strip()
    if tel.startswith("+"):
        return tel[1:].isdigit()
    return tel.isdigit()

Versión integrada (todo junto, lista para ejecutar)

Si quieres probar el mini-reto completo en una sola pieza, aquí tienes una versión compacta que une lo anterior.

def buscar_indice_por_nombre(agenda, nombre):
    nombre = nombre.strip().lower()
    for i, contacto in enumerate(agenda):
        if contacto[0].strip().lower() == nombre:
            return i
    return -1

def actualizar_contacto(agenda, idx):
    nombre_actual, tel_actual, email_actual = agenda[idx]
    print("Deja en blanco para mantener el valor actual.")
    nuevo_nombre = input(f"Nombre ({nombre_actual}): ").strip()
    nuevo_tel = input(f"Teléfono ({tel_actual}): ").strip()
    nuevo_email = input(f"Email ({email_actual}): ").strip()

    if nuevo_nombre != "":
        agenda[idx][0] = nuevo_nombre
    if nuevo_tel != "":
        agenda[idx][1] = nuevo_tel
    if nuevo_email != "":
        agenda[idx][2] = nuevo_email

agenda = []

while True:
    print("\n=== AGENDA SIMPLE ===")
    print("1) Crear contacto")
    print("2) Ver contactos")
    print("3) Actualizar contacto")
    print("4) Eliminar contacto")
    print("5) Salir")

    opcion = input("Elige una opción: ").strip()

    if opcion == "1":
        nombre = input("Nombre: ").strip()
        telefono = input("Teléfono: ").strip()
        email = input("Email: ").strip()

        if nombre == "":
            print("Nombre obligatorio.")
        else:
            idx = buscar_indice_por_nombre(agenda, nombre)
            if idx != -1:
                print("Ya existe un contacto con ese nombre.")
            else:
                agenda.append([nombre, telefono, email])
                print("Contacto creado.")

    elif opcion == "2":
        if len(agenda) == 0:
            print("Agenda vacía.")
        else:
            print("\n--- CONTACTOS ---")
            for i, contacto in enumerate(agenda, start=1):
                nombre, telefono, email = contacto
                print(f"{i}. {nombre} | {telefono} | {email}")

    elif opcion == "3":
        if len(agenda) == 0:
            print("No hay contactos para actualizar.")
        else:
            modo = input("Actualizar por (n)úmero o por (nombre): ").strip().lower()
            if modo == "n":
                n = input("Número de contacto: ").strip()
                if not n.isdigit():
                    print("Debes escribir un número.")
                else:
                    pos = int(n) - 1
                    if pos < 0 or pos >= len(agenda):
                        print("Número fuera de rango.")
                    else:
                        actualizar_contacto(agenda, pos)
                        print("Contacto actualizado.")
            else:
                nombre = input("Nombre a actualizar: ").strip()
                idx = buscar_indice_por_nombre(agenda, nombre)
                if idx == -1:
                    print("No encontrado.")
                else:
                    actualizar_contacto(agenda, idx)
                    print("Contacto actualizado.")

    elif opcion == "4":
        if len(agenda) == 0:
            print("No hay contactos para eliminar.")
        else:
            nombre = input("Nombre a eliminar: ").strip()
            idx = buscar_indice_por_nombre(agenda, nombre)
            if idx == -1:
                print("No encontrado.")
            else:
                contacto = agenda[idx]
                confirm = input(f"¿Eliminar a {contacto[0]}? (s/n): ").strip().lower()
                if confirm == "s":
                    agenda.pop(idx)
                    print("Contacto eliminado.")
                else:
                    print("Operación cancelada.")

    elif opcion == "5":
        break

    else:
        print("Opción no válida.")

Ahora responde el ejercicio sobre el contenido:

En la función de actualizar, ¿qué sucede si el usuario deja un campo en blanco al ingresar los nuevos datos?

¡Tienes razón! Felicitaciones, ahora pasa a la página siguiente.

¡Tú error! Inténtalo de nuevo.

La lógica de actualización solo asigna un nuevo valor si el input no está vacío. Si el usuario deja un campo en blanco, se mantiene el valor que ya tenía el contacto.

Siguiente capítulo

Mini-reto: juego de adivinanza con intentos y pistas

Arrow Right Icon
Descarga la aplicación para obtener una certificación gratuita y escuchar cursos en segundo plano, incluso con la pantalla apagada.