Contenido del curso
Fundamentos de Python
Los fundamentos de Python incluyen la sintaxis (sangría para bloques de código), los tipos de datos básicos (numéricos, booleanos, cadenas de texto), las variables, el control de flujo (condicionales como if/elif/else y bucles como for/while), y las funciones (bloques de código reutilizables).
0/6
Operadores y Control de Flujo
Los operadores en Python se clasifican en varios tipos (aritméticos, de comparación, lógicos, de asignación, etc.), mientras que el control de flujo se refiere al orden en que se ejecutan las instrucciones, modificándolo con estructuras como if, elif, else (condicionales), y for o while (bucles). Las instrucciones break, continue y pass también controlan el flujo dentro de los bucles.
0/3
Funciones y Manejo de Errores
Las funciones en Python son bloques de código reutilizables, mientras que el manejo de errores (excepciones) se hace con los bloques try, except, else y finally para gestionar errores de ejecución y evitar que el programa se detenga abruptamente. try ejecuta un código, except lo captura si ocurre un error específico, else se ejecuta si no hay error y finally se ejecuta siempre, haya o no error.
0/3
Estructuras de Datos
Las estructuras de datos principales en Python son las listas, tuplas, diccionarios y conjuntos. Estos tipos de datos se diferencian por su mutabilidad (si sus elementos se pueden cambiar después de su creación) y si mantienen el orden de los elementos. Las listas son ordenadas y mutables, mientras que las tuplas son ordenadas e inmutables. Los diccionarios son colecciones no ordenadas de pares clave-valor, y los conjuntos son colecciones desordenadas de elementos únicos.
0/5
Programación Orientada a Objetos (POO)
La Programación Orientada a Objetos (POO) en Python es un paradigma que organiza el código en torno a objetos, que son instancias de clases. Las clases actúan como plantillas que definen los atributos (datos) y métodos (comportamientos) de los objetos, permitiendo crear programas más modularizados, reutilizables y fáciles de mantener. Python soporta conceptos clave de la POO como la herencia, el encapsulamiento y el polimorfismo.
0/5
Ambientes virtuales
Un entorno virtual de Python es un espacio aislado que permite instalar paquetes y dependencias específicos para un proyecto concreto sin afectar a otras aplicaciones o a la instalación global de Python. Se crea una carpeta con una instalación de Python y una copia local de pip dentro de este entorno, lo que permite a cada proyecto tener sus propias bibliotecas y versiones, evitando así conflictos entre diferentes proyectos que puedan requerir versiones distintas de la misma librería.
0/1
Archivos
El manejo de archivos en Python se realiza principalmente usando la función open() para abrir un archivo y los métodos read(), write(), append() y close() para manipularlo. Es crucial gestionar los archivos adecuadamente, cerrándolos para liberar recursos, aunque es más recomendable usar la sentencia with, que cierra el archivo automáticamente. Python permite trabajar con archivos de texto y binarios, así como con distintos modos de apertura como 'r' (solo lectura), 'w' (escritura/sobreescritura), y 'a' (añadir).
0/1
Módulos y Librerías Estándar
Un "módulo" en Python se refiere a dos conceptos distintos: un archivo .py con código que se puede importar para reutilizar funciones, clases y variables, y el operador % que calcula el residuo de una división entera. Ambos son útiles para organizar el código y resolver problemas matemáticos, respectivamente.
0/2
Hilos y tareas en Python
En Python, los hilos (threads) son secuencias de ejecución dentro de un proceso que permiten la concurrencia, ejecutando tareas simultáneamente para aprovechar mejor los recursos del sistema. Las tareas son las unidades de trabajo a realizar, como descargar archivos o procesar datos. Se utilizan para manejar operaciones que implican espera (I/O-bound) de forma eficiente, permitiendo que una aplicación no se bloquee mientras espera. Para ello, se usa el módulo threading, se crean objetos Thread que representan las tareas, se inician con .start() y se pueden sincronizar con mecanismos como Lock para evitar conflictos.
0/1
Curso de Programación en Pythón 3.

    🔁 Parte 1 — Bucle for, for con range() y recorrido de colecciones

    El bucle for en Python recorre elementos de un iterable (listas, tuplas, cadenas, diccionarios, sets, rangos, etc.).
    A diferencia de otros lenguajes, for en Python no usa un índice numérico por defecto; en su lugar, itera directamente sobre los elementos.


    ✅ Sintaxis básica de for

    
    # Recorrer una lista de nombres
    nombres = ["Ana", "Luis", "Sofía"]
    
    for nombre in nombres:
        print("Hola,", nombre)
      

    Lee la frase como: “para cada nombre en nombres, ejecuta el bloque”. Es directo y muy legible.


    📦 ¿Qué es un iterable?

    Un iterable es un objeto que se puede recorrer elemento a elemento: listas, tuplas, diccionarios, conjuntos, cadenas, objetos de range(), etc.
    Más adelante (Parte 5) veremos iter(), enumerate() y zip() para iteración avanzada.


    📚 Recorrer las colecciones más comunes

    ColecciónQué recorreEjemplo
    Lista / TuplaElementos en orden
    
    numeros = [10, 20, 30]
    for n in numeros:
        print(n)
            
    Cadena (str)Caracteres
    
    texto = "Python"
    for c in texto:
        print(c)
            
    Diccionario (dict)Claves por defecto
    
    persona = {"nombre": "Javi", "edad": 25}
    for clave in persona:
        print(clave, "→", persona[clave])
    # Mejor explícito:
    for clave, valor in persona.items():
        print(clave, "=", valor)
            
    Conjunto (set)Elementos únicos (sin orden garantizado)
    
    colores = {"rojo", "verde", "azul"}
    for c in colores:
        print(c)  # el orden puede variar
            

    🎯 Iteración indexada: cuándo y cómo

    Si necesitas el índice, evita contadores manuales. Lo más claro es usar enumerate() (lo verás en detalle en la Parte 5). Vista rápida:

    
    frutas = ["manzana", "pera", "uva"]
    for i, fruta in enumerate(frutas):
        print(i, fruta)
      

    Solo usa índices cuando sea necesario; por defecto, itera por elementos (código más limpio y menos errores).


    📏 range(): generador de secuencias numéricas

    range() crea una secuencia de enteros eficiente en memoria. Muy útil para repetir acciones N veces o generar índices.

    FirmaSignificadoEjemploSecuencia
    range(stop)0 hasta stop-1range(5)0,1,2,3,4
    range(start, stop)start hasta stop-1range(2, 6)2,3,4,5
    range(start, stop, step)Salto step (positivo o negativo)range(10, 0, -2)10,8,6,4,2
    
    # Repetir 5 veces
    for _ in range(5):
        print("¡Hola!")
    
    # Recorrer índices de una lista (si lo necesitas)
    frutas = ["manzana", "pera", "uva"]
    for i in range(len(frutas)):
        print(i, frutas[i])
    
    # Contar hacia atrás
    for n in range(5, 0, -1):
        print(n)
      
    Ojo “off-by-one”: el límite superior de range() es exclusivo. range(5) llega hasta 4.

    🧠 forelse: el else de los bucles

    El bloque else se ejecuta si el for termina sin encontrar un break. Útil para búsquedas.

    
    numeros = [2, 4, 6, 8]
    buscado = 5
    
    for n in numeros:
        if n == buscado:
            print("Encontrado")
            break
    else:
        print("No encontrado")  # Solo se ejecuta si NO hubo break
      

    🧼 Patrones seguros al iterar

    1) No modifiques una lista mientras la recorres

    Si necesitas mutarla, itera sobre una copia o construye una nueva lista.

    
    # 👎 Riesgo de comportamientos extraños
    for x in lista:
        if condicion(x):
            lista.remove(x)
    
    # 👍 Seguro (copia superficial)
    for x in lista[:]:
        if condicion(x):
            lista.remove(x)
    
    # 👍 Mejor: filtra y reasigna
    lista = [x for x in lista if not condicion(x)]
      

    2) Itera por elementos, no por índices (cuando sea posible)

    
    # 👎 Verboso e innecesario
    for i in range(len(nombres)):
        print(nombres[i])
    
    # 👍 Idiomático
    for nombre in nombres:
        print(nombre)
      

    3) Costes de operaciones dentro del bucle

    Evita repetir cálculos o accesos caros dentro del bucle; guarda referencias locales.

    
    # 👎 Llamada repetida costosa
    for i in range(len(data())):
        procesar(data()[i])
    
    # 👍 Cachea resultados
    d = data()
    for item in d:
        procesar(item)
      

    🧮 Ejemplos típicos con for

    A) Sumar elementos

    
    numeros = [3, 5, 7]
    suma = 0
    for n in numeros:
        suma += n
    print("Suma =", suma)
      

    B) Contar elementos que cumplen una condición

    
    palabras = ["python", "java", "go", "php"]
    contador = 0
    for p in palabras:
        if len(p) >= 4:
            contador += 1
    print("Con 4+ letras:", contador)
      

    C) Recorrer un diccionario (claves, valores, pares)

    
    config = {"host": "localhost", "port": 5432}
    
    for k in config:                # claves
        print(k)
    
    for v in config.values():       # valores
        print(v)
    
    for k, v in config.items():     # pares
        print(k, "=", v)
      

    🌱 Introducción a comprensiones (teaser)

    Las comprensiones crean colecciones a partir de iterables de forma concisa. Las verás en profundidad más adelante, pero aquí va una muestra:

    
    # Lista con cuadrados de 1..5
    cuadrados = [n * n for n in range(1, 6)]
    print(cuadrados)  # [1, 4, 9, 16, 25]
      
    Regla de oro: si la comprensión se vuelve larga o compleja, vuelve a un for tradicional por legibilidad.

    🧪 Mini-ejercicios

    1. Imprime los números del 1 al 10 usando range(). Luego, del 10 al 1 con paso negativo.
    2. Recorre la cadena "Programación" y cuenta cuántas vocales contiene.
    3. Dado el diccionario {"a":1,"b":2,"c":3}, genera una salida “a=1, b=2, c=3”.
    4. Crea una lista con los múltiplos de 3 entre 1 y 30 usando for y otra con comprensión; compara.
    5. Usa for ... else para buscar un elemento en una lista y reportar si no está.

    🏁 Conclusión

    Has visto el corazón de la iteración en Python: for sobre iterables y range() para secuencias numéricas.
    En la Ahora abordarás while y la forma idiomática de simular do-while en Python, con patrones de ruptura controlada.

    ⏳ Parte 2 — Bucle while y simulación de do-while

    El bucle while repite un bloque de código mientras una condición sea verdadera.
    A diferencia de for, que recorre elementos de un iterable, while es ideal cuando no conoces de antemano cuántas iteraciones necesitarás (por ejemplo, leer entradas hasta que el usuario escriba “salir”).


    ✅ Sintaxis básica de while

    
    contador = 1
    while contador <= 5:
        print("Iteración", contador)
        contador += 1
      
    • La condición se evalúa antes de cada vuelta.
    • Hay que actualizar el estado (ej. incrementar contadores) para evitar bucles infinitos.

    🧭 Casos de uso típicos

    PatrónDescripciónEjemplo
    ContadorRepite N veces con una condición numérica
    
    i = 0
    while i < 3:
        print(i)
        i += 1
            
    CentinelaRepite hasta leer un valor “sentinel” (p. ej., «salir»)
    
    linea = input(">>> ")
    while linea != "salir":
        print("Eco:", linea)
        linea = input(">>> ")
            
    ValidaciónInsiste hasta que la entrada sea válida
    
    edad = input("Edad: ")
    while not (edad.isdigit() and int(edad) > 0):
        edad = input("Edad válida (>0): ")
    edad = int(edad)
    print("OK, edad =", edad)
            

    🧠 whileelse: el else de los bucles

    El bloque else se ejecuta cuando el while termina sin un break. Útil para búsquedas o intentos acotados.

    
    objetivo = 37
    n = 1
    
    while n * n <= 1000:
        if n * n == objetivo:
            print("Raíz exacta encontrada:", n)
            break
        n += 1
    else:
        print("No hay raíz entera para", objetivo)
      

    🧪 Simulación de do-while en Python

    Python no tiene do-while nativo (ejecuta al menos una vez y luego comprueba). Se emula con while True + break al final del ciclo.

    Patrón general

    
    while True:
        # 1) Hacer trabajo al menos una vez
        comando = input("Comando (salir para terminar): ")
        # 2) Validar / decidir
        if comando == "salir":
            break
        # 3) Continuar si procede
        print("Procesado:", comando)
      

    Ejemplo: leer un número (al menos una vez) y validar

    
    while True:
        dato = input("Introduce un número positivo: ")
        if dato.replace('.', '', 1).isdigit() and float(dato) > 0:
            numero = float(dato)
            break
        print("Valor no válido. Intenta de nuevo.")
    
    print("Número OK:", numero)
      

    🛡️ Evitar bucles infinitos

    • Asegúrate de que algo cambia en cada iteración (contadores, lectura de nueva entrada, etc.).
    • Si usas while True, define condiciones de salida claras con break.
    
    # 👎 Infinite loop potencial (i nunca cambia)
    i = 0
    while i < 5:
        print(i)
        # falta: i += 1
    
    # 👍 Corrección
    i = 0
    while i < 5:
        print(i)
        i += 1
      

    ⚙️ break, continue y pass con while (vista rápida)

    
    # break: sale del bucle
    i = 0
    while i < 10:
        if i == 5:
            break
        print(i)
        i += 1
    
    # continue: salta a la siguiente iteración
    i = 0
    while i < 5:
        i += 1
        if i % 2 == 0:
            continue
        print("Impar:", i)
    
    # pass: placeholder (no hace nada, útil como “TODO”)
    while False:
        pass
      

    Verás estos keywords en detalle en la Parte 3.


    🧩 Patrones prácticos de while

    1) Reintentos con límite

    
    intentos = 0
    MAX_INTENTOS = 3
    
    while intentos < MAX_INTENTOS:
        pwd = input("Password: ")
        if pwd == "secreto":
            print("Acceso concedido")
            break
        intentos += 1
    else:
        print("Cuenta bloqueada por demasiados intentos")
      

    2) Acumular hasta centinela

    
    total = 0.0
    while True:
        s = input("Importe (ENTER para terminar): ").strip()
        if s == "":
            break
        if s.replace('.', '', 1).isdigit():
            total += float(s)
        else:
            print("Número inválido:", s)
    
    print("Total =", total)
      

    3) Polling con espera (simulado)

    
    import time
    
    estado = "pendiente"
    reintentos = 0
    
    while estado != "completado" and reintentos < 5:
        print("Consultando estado...")
        time.sleep(1)          # espera 1 segundo (ejemplo)
        # Simulación: cambia estado en el 3er intento
        reintentos += 1
        if reintentos == 3:
            estado = "completado"
    
    print("Estado final:", estado)
      

    🧼 Buenas prácticas con while

    • Prefiere for cuando iteras colecciones; usa while para condiciones abiertas o bucles controlados por eventos.
    • Declara claramente el estado que gobierna la salida del bucle.
    • Evita condiciones complejas: extrae booleanos con nombre (cond_ok, entradas_restantes…).
    • Para “al menos una vez”, usa el patrón while True + break (do-while).

    🧪 Mini-ejercicios

    1. Solicita números al usuario y calcula la media. Termina cuando escriba “fin”.
    2. Pide una contraseña y confirma que tenga al menos 8 caracteres, 1 mayúscula y 1 número. Insiste hasta que cumpla.
    3. Implementa un contador regresivo desde un número N hasta 0 con while.
    4. Simula un do-while que pida “¿Deseas continuar? (s/n)” y solo acepte s o n.
    5. Escribe un while ... else que busque un valor en una lista y notifique si no aparece.

    🏁 Conclusión

    El bucle while es tu aliado cuando la iteración depende de una condición dinámica.
    Con el patrón do-while emulado, cubres la necesidad de ejecutar al menos una vez.
    En la Ahora profundizaremos en las palabras clave break, continue y pass, y cómo controlan el flujo dentro de cualquier bucle.

    ⚙️ Parte 3 — Palabras clave break, continue y pass

    Python ofrece tres instrucciones especiales para controlar el flujo dentro de un bucle:
    break, continue y pass.
    Con ellas puedes detener, saltar o mantener la estructura del bucle según las condiciones.


    🚪 break: salir del bucle inmediatamente

    break interrumpe el bucle actual, incluso si la condición todavía se cumple.
    Es útil para terminar cuando se encuentra un resultado, error o evento esperado.

    
    # Buscar un número en una lista
    numeros = [2, 4, 6, 8, 10]
    buscado = 6
    
    for n in numeros:
        if n == buscado:
            print("Encontrado:", n)
            break
        print("Comprobando", n)
    
    print("Fin del bucle")
      

    📌 Aquí el break detiene el bucle en cuanto encuentra el valor buscado.


    💡 Con while

    
    contador = 0
    while True:
        contador += 1
        print("Intento", contador)
        if contador == 3:
            print("¡Máximo alcanzado!")
            break
      

    En un while True, break es la condición de salida. Es la forma más común de crear bucles con fin controlado manualmente.


    🧠 Uso combinado con else

    Cuando se usa con for o while, el bloque else solo se ejecuta si no hubo un break.

    
    for n in [1, 2, 3, 4]:
        if n == 99:
            print("Encontrado")
            break
    else:
        print("No se encontró el valor")
      

    🔁 continue: saltar a la siguiente iteración

    continue interrumpe la iteración actual y pasa directamente a la siguiente vuelta del bucle.
    Se usa para ignorar casos que no queremos procesar, sin detener el bucle completo.

    
    # Mostrar solo números impares
    for n in range(1, 8):
        if n % 2 == 0:
            continue
        print("Impar:", n)
      

    ✅ Los valores pares se saltan; el resto se procesan normalmente.

    Ejemplo con while

    
    n = 0
    while n < 5:
        n += 1
        if n == 3:
            continue  # salta la impresión de 3
        print("Número:", n)
      

    🧩 Comparación entre break y continue

    Palabra claveQué haceEjemplo visual
    breakDetiene todo el bucle
    
    for i in range(5):
        if i == 2:
            break
        print(i)
    # Output: 0, 1
            
    continueSalta a la siguiente iteración
    
    for i in range(5):
        if i == 2:
            continue
        print(i)
    # Output: 0, 1, 3, 4
            

    🪟 pass: “no hacer nada” (placeholder)

    pass sirve como bloque vacío: mantiene la estructura del código cuando aún no hay implementación.
    Python exige que los bloques (if, for, def…) contengan al menos una instrucción.
    Si todavía no tienes nada que ejecutar, pass evita errores de sintaxis.

    
    # Ejemplo en un if
    edad = 17
    if edad >= 18:
        print("Mayor de edad")
    else:
        pass  # pendiente de implementar
      

    También se usa en clases o funciones que están “por definir”.

    
    def procesar_datos():
        pass  # TODO: implementar lógica más adelante
    
    class Usuario:
        pass  # estructura vacía temporal
      

    ⚙️ Ejemplo práctico integrador

    Veamos cómo se combinan break, continue y pass en un programa con flujo controlado:

    
    # Procesar una lista de tareas
    tareas = ["leer", "dormir", "escribir", "salir", "pasear"]
    
    for tarea in tareas:
        if tarea == "salir":
            print("🚪 Fin de la sesión")
            break
        elif tarea == "dormir":
            print("😴 Saltando tarea no productiva")
            continue
        elif tarea == "escribir":
            pass  # futuro: guardar en base de datos
        print("✅ Ejecutando:", tarea)
      

    💡 Salida esperada:

    
    ✅ Ejecutando: leer
    😴 Saltando tarea no productiva
    ✅ Ejecutando: escribir
    🚪 Fin de la sesión
      

    🧠 Cuándo usar cada una

    InstrucciónPropósitoUso típico
    breakSalir del bucle completamenteCuando se cumple una condición o se detecta un error
    continueSaltar a la siguiente vueltaIgnorar casos no válidos o que no aplican
    passMarcador de posiciónBloques vacíos en desarrollo

    🧩 Errores comunes

    • ❌ Olvidar break en un while True → bucle infinito.
    • ❌ Usar continue sin actualizar variables → misma condición repetida.
    • ❌ Colocar pass creyendo que ejecuta algo → no hace nada realmente.

    🧪 Mini-ejercicios

    1. Recorre una lista de nombres y detente con break cuando aparezca “fin”.
    2. Imprime los números del 1 al 10, pero omite los múltiplos de 3 con continue.
    3. Crea una función menu() que repita opciones hasta que el usuario escriba “salir”. Usa while True y break.
    4. Declara una clase vacía Persona y una función pendiente() con pass dentro.
    5. Haz una búsqueda en una lista usando for ... else y break si se encuentra el valor.

    🏁 Conclusión

    Las palabras clave break, continue y pass te permiten afinar el control de flujo en cualquier bucle.
    Dominar su uso te dará un código más expresivo, flexible y limpio.

    En la Ahora aprenderás a combinar bucles anidados (uno dentro de otro) y cómo aplicar estas palabras clave para optimizar tus recorridos más complejos.

    🔂 Parte 4 — Bucles anidados y control de flujo

    Los bucles anidados permiten ejecutar un bucle dentro de otro.
    Son útiles cuando se trabaja con estructuras bidimensionales (listas de listas, matrices, tablas, cuadrículas, etc.)
    o cuando una tarea necesita repetir otra tarea interna varias veces.


    ✅ Sintaxis general

    
    for externo in iterable_externo:
        for interno in iterable_interno:
            # código a ejecutar por cada combinación
            ...
      

    Esto se traduce como: “por cada elemento del bucle externo, recorre todos los elementos del bucle interno”.
    El número total de iteraciones es el producto de ambos tamaños (len(outer) * len(inner)).


    📦 Ejemplo 1 — Tabla de multiplicar

    
    for i in range(1, 4):        # bucle externo
        for j in range(1, 4):    # bucle interno
            print(f"{i} × {j} = {i*j}")
        print("-- Fin del bloque de", i)
      

    💡 Salida:

    
    1 × 1 = 1
    1 × 2 = 2
    1 × 3 = 3
    -- Fin del bloque de 1
    2 × 1 = 2
    2 × 2 = 4
    2 × 3 = 6
    -- Fin del bloque de 2
    ...
      

    Cada ciclo externo (i) activa un recorrido completo del ciclo interno (j).


    📊 Ejemplo 2 — Recorrer listas de listas (matriz)

    
    # Matriz de 3x3
    matriz = [
        [1, 2, 3],
        [4, 5, 6],
        [7, 8, 9]
    ]
    
    for fila in matriz:
        for valor in fila:
            print(valor, end=" ")
        print()
      

    💡 Salida:

    
    1 2 3
    4 5 6
    7 8 9
      

    ✅ El bucle externo recorre las filas, y el interno los valores dentro de cada fila.


    🧠 Ejemplo 3 — Diccionarios anidados

    Cuando un diccionario contiene otros diccionarios, puedes usar bucles anidados para recorrer las claves y valores internos.

    
    usuarios = {
        "javier": {"rol": "admin", "activo": True},
        "ana": {"rol": "editor", "activo": False}
    }
    
    for usuario, datos in usuarios.items():
        print(f"👤 {usuario}:")
        for clave, valor in datos.items():
            print(f"  {clave} = {valor}")
      

    💡 Salida:

    
    👤 javier:
      rol = admin
      activo = True
    👤 ana:
      rol = editor
      activo = False
      

    🔍 Ejemplo 4 — Búsqueda con break en bucles anidados

    El uso de break en bucles anidados solo rompe el bucle interno, no el externo.
    Si quieres detener ambos, debes usar una bandera o una función auxiliar.

    
    matriz = [
        [1, 2, 3],
        [4, 5, 6],
        [7, 8, 9]
    ]
    
    buscado = 5
    encontrado = False
    
    for fila in matriz:
        for valor in fila:
            if valor == buscado:
                print("Encontrado:", valor)
                encontrado = True
                break  # rompe el bucle interno
        if encontrado:
            break  # rompe el externo
    
    print("Búsqueda completada")
      

    💡 Salida: detiene ambos bucles al encontrar el número.


    ⚙️ Ejemplo 5 — Generar coordenadas (x, y)

    Los bucles anidados son muy útiles para generar combinaciones o pares de valores.

    
    for x in range(3):
        for y in range(3):
            print(f"({x}, {y})")
      

    📏 Total de iteraciones: 3 × 3 = 9.


    💡 Control del flujo en bucles anidados

    Palabra claveEfecto en bucles anidadosEjemplo
    breakSale del bucle interno
    
    for i in range(3):
        for j in range(3):
            if j == 1:
                break
            print(i, j)
            
    continueSalta la iteración interna actual
    
    for i in range(3):
        for j in range(3):
            if j == 1:
                continue
            print(i, j)
            
    passNo hace nada (reserva de bloque)
    
    for i in range(2):
        for j in range(2):
            pass  # código pendiente
            

    📦 Ejemplo 6 — Patrón con bucles anidados (pirámide)

    
    # Pirámide simple
    filas = 5
    for i in range(1, filas + 1):
        for j in range(i):
            print("*", end="")
        print()
      

    💡 Salida:

    
    *
    **
    ***
    ****
    *****
      

    El bucle interno imprime los asteriscos, y el externo controla la cantidad de filas.


    🧮 Ejemplo 7 — Matriz transpuesta (for anidados)

    Transformar filas en columnas con bucles anidados:

    
    matriz = [
        [1, 2, 3],
        [4, 5, 6]
    ]
    filas = len(matriz)
    columnas = len(matriz[0])
    
    transpuesta = []
    for j in range(columnas):
        fila_nueva = []
        for i in range(filas):
            fila_nueva.append(matriz[i][j])
        transpuesta.append(fila_nueva)
    
    print(transpuesta)
      

    💡 Salida: [[1, 4], [2, 5], [3, 6]]


    🧩 Ejemplo 8 — Triple bucle

    También se pueden anidar tres bucles o más, pero esto debe hacerse solo cuando sea necesario (afecta el rendimiento).

    
    for x in range(2):
        for y in range(2):
            for z in range(2):
                print(f"({x},{y},{z})")
      

    📏 Total de iteraciones: 2 × 2 × 2 = 8.


    🧠 Buenas prácticas con bucles anidados

    • ⚙️ Usa bucles anidados solo cuando sea necesario. Evalúa si puedes usar funciones, listas por comprensión o itertools.
    • 📉 La complejidad aumenta rápidamente: un doble bucle es O(n²), un triple O(n³).
    • 💡 Usa break y continue para acortar ejecuciones innecesarias.
    • 🧱 Refactoriza: si el bucle interno crece, muévelo a una función auxiliar.
    • 🧩 Usa nombres descriptivos en lugar de i, j, k si el contexto lo permite.

    🧪 Mini-ejercicios

    1. Genera la tabla de multiplicar del 1 al 10 usando bucles anidados.
    2. Imprime una cuadrícula 5×5 de coordenadas (x, y).
    3. Dada una lista de listas, muestra la suma de cada sublista.
    4. Convierte una matriz 3×3 en su transpuesta usando bucles.
    5. Crea un triángulo de números con doble bucle, como:
      
      1
      1 2
      1 2 3
      1 2 3 4
            

    🏁 Conclusión

    Los bucles anidados son potentes y flexibles, pero deben usarse con prudencia.
    Son el pilar del procesamiento de estructuras complejas como matrices, tablas y datos multidimensionales.
    En la Ahora aprenderás a dominar la iteración avanzada con enumerate(), zip() e iteradores personalizados para escribir código aún más eficiente y expresivo.

    ⚙️ Parte 5 — Iteración avanzada: iter(), enumerate() y zip()

    Hasta ahora has aprendido a recorrer colecciones con for y while.
    En esta sección veremos técnicas avanzadas de iteración que hacen tu código más legible, eficiente y profesional:

    • iter() — Convierte objetos en iteradores manuales.
    • enumerate() — Añade índices automáticamente durante un bucle.
    • zip() — Combina varios iterables en paralelo.

    🧩 1. iter(): crear un iterador manualmente

    Un iterador es un objeto que devuelve elementos uno a uno cuando se usa en un bucle.
    La función iter() devuelve un iterador a partir de una colección o secuencia.

    
    frutas = ["manzana", "pera", "uva"]
    it = iter(frutas)   # crea un iterador
    
    print(next(it))  # manzana
    print(next(it))  # pera
    print(next(it))  # uva
    # print(next(it)) → StopIteration
      

    💡 Cuando se acaba el iterador, lanza una excepción StopIteration.
    Por eso los bucles for la gestionan automáticamente.


    🔁 Ejemplo: iterador manual en un bucle while

    
    numeros = [10, 20, 30]
    it = iter(numeros)
    
    while True:
        try:
            n = next(it)
            print("Número:", n)
        except StopIteration:
            print("Fin del iterador")
            break
      

    ✅ Este patrón es útil cuando necesitas control total del flujo (por ejemplo, pausar o retomar iteraciones).


    🧠 ¿Qué es un iterable y un iterador?

    TérminoDescripciónEjemplo
    IterableObjeto que puede ser recorrido (tiene __iter__())Listas, tuplas, cadenas, sets, diccionarios
    IteradorObjeto que devuelve elementos sucesivos (__next__())Resultado de iter()
    
    lista = [1, 2, 3]
    print(hasattr(lista, "__iter__"))   # True → iterable
    it = iter(lista)
    print(hasattr(it, "__next__"))      # True → iterador
      

    🔢 2. enumerate(): índices automáticos

    Cuando necesitas tanto el índice como el valor durante un bucle, usa enumerate().
    Es más limpio que usar range(len(...)) y evita errores de índice.

    
    nombres = ["Ana", "Luis", "Sofía"]
    
    for i, nombre in enumerate(nombres):
        print(i, "→", nombre)
      

    💡 enumerate() devuelve pares (índice, elemento) en cada iteración.

    👉 Índice personalizado

    
    for i, nombre in enumerate(nombres, start=1):
        print(f"{i}. {nombre}")
      

    📋 Salida:

    
    1. Ana
    2. Luis
    3. Sofía
      

    🧠 Ejemplo: detección con índice

    
    letras = ["a", "b", "x", "d", "e"]
    
    for i, letra in enumerate(letras):
        if letra == "x":
            print("Encontrado en posición", i)
            break
      

    ✅ Ideal para búsquedas o depuración con posición.


    🔗 3. zip(): iterar en paralelo

    La función zip() combina varios iterables, devolviendo tuplas con los elementos correspondientes.
    Es perfecta para recorrer listas “en paralelo”.

    
    nombres = ["Ana", "Luis", "Sofía"]
    edades = [20, 25, 22]
    
    for nombre, edad in zip(nombres, edades):
        print(nombre, "tiene", edad, "años")
      

    💡 Salida:

    
    Ana tiene 20 años
    Luis tiene 25 años
    Sofía tiene 22 años
      

    🧮 Listas de distinta longitud

    zip() se detiene en el iterable más corto.

    
    a = [1, 2, 3]
    b = ["uno", "dos"]
    
    for x, y in zip(a, b):
        print(x, y)
      

    📏 Salida: solo 2 pares (1-uno, 2-dos).

    Para llenar los huecos, usa itertools.zip_longest():

    
    from itertools import zip_longest
    
    for x, y in zip_longest(a, b, fillvalue="(sin valor)"):
        print(x, y)
      

    💡 Salida:

    
    1 uno
    2 dos
    3 (sin valor)
      

    🧠 Ejemplo práctico: combinar datos

    
    nombres = ["Ana", "Luis", "Sofía"]
    notas = [9, 7, 10]
    
    registro = dict(zip(nombres, notas))
    print(registro)
      

    💡 Salida: {'Ana': 9, 'Luis': 7, 'Sofía': 10}

    Usos comunes:

    • Construir diccionarios rápidamente.
    • Combinar listas relacionadas (campos y valores, claves y resultados, etc.).
    • Comparar colecciones en paralelo.

    ⚙️ 4. Combinación: enumerate() + zip()

    Puedes usar ambos para recorrer pares con índice incluido.

    
    frutas = ["Manzana", "Pera", "Uva"]
    colores = ["Roja", "Verde", "Morada"]
    
    for i, (fruta, color) in enumerate(zip(frutas, colores), start=1):
        print(f"{i}. {fruta} → {color}")
      

    📋 Salida:

    
    1. Manzana → Roja
    2. Pera → Verde
    3. Uva → Morada
      

    🧠 5. Iteradores personalizados (nivel intermedio)

    Además de usar los iteradores integrados, puedes crear tus propios iteradores definiendo las funciones __iter__() y __next__().

    
    class Contador:
        def __init__(self, inicio, fin):
            self.actual = inicio
            self.fin = fin
    
        def __iter__(self):
            return self
    
        def __next__(self):
            if self.actual > self.fin:
                raise StopIteration
            valor = self.actual
            self.actual += 1
            return valor
    
    # Uso
    for n in Contador(1, 5):
        print(n)
      

    💡 Salida: 1 2 3 4 5

    ✅ Este patrón es útil para construir objetos que generen datos bajo demanda (p. ej., streams, generadores, lectura de archivos).


    📊 Resumen visual

    FunciónPropósitoDevuelveEjemplo
    iter()Convierte un iterable en iteradorIterador (control manual)it = iter([1,2,3])
    enumerate()Agrega índice automáticoTuplas (índice, valor)for i,v in enumerate(lista):
    zip()Combina varios iterablesTuplas de valores combinadosfor a,b in zip(x, y):

    🧪 Mini-ejercicios

    1. Usa iter() y next() para recorrer manualmente una lista de frutas.
    2. Recorre una lista con enumerate() y muestra el índice y el valor.
    3. Combina dos listas (nombres y ciudades) usando zip() y crea un diccionario.
    4. Modifica el ejercicio anterior para que muestre también el número de registro con enumerate().
    5. Crea una clase ContadorPares que itere solo por números pares entre dos valores.

    🏁 Conclusión

    Las herramientas iter(), enumerate() y zip() son pilares de la iteración avanzada en Python.
    Permiten recorrer datos de forma elegante, clara y eficiente, reemplazando bucles tradicionales con código más expresivo.

    En la Ahora integraremos todo lo aprendido (for, while,, do while, break, continue, iter, enumerate, zip) en ejercicios prácticos y casos reales de automatización.

    🏋️ Parte 6 — Ejercicios prácticos, rendimiento y resumen final

    En esta última parte pondrás a prueba todo lo aprendido sobre bucles y control de iteración.
    Realizaremos ejercicios integradores que combinan for, while, break, continue, zip(), enumerate() e incluso iter().
    También aprenderás a escribir bucles más eficientes y a identificar cuándo simplificar el código.


    🧩 Ejercicio 1 — Calculadora de notas con for y zip()

    Combina dos listas con zip() y calcula el promedio general de los estudiantes.

    
    nombres = ["Ana", "Luis", "Sofía"]
    notas = [8.5, 9.2, 7.8]
    
    suma = 0
    for nombre, nota in zip(nombres, notas):
        print(f"{nombre}: {nota}")
        suma += nota
    
    promedio = suma / len(notas)
    print("Promedio general:", round(promedio, 2))
      

    Conceptos aplicados: for, zip(), acumulación y formato de salida.


    🔁 Ejercicio 2 — Sistema de autenticación con while y break

    
    usuarios = {"javier": "python123", "ana": "pass2025"}
    
    while True:
        user = input("Usuario: ")
        pwd = input("Contraseña: ")
    
        if user in usuarios and usuarios[user] == pwd:
            print("✅ Acceso concedido")
            break
        else:
            print("❌ Credenciales inválidas, intenta de nuevo.")
      

    💡 Este patrón es típico de menús y sistemas de login en consola.


    📦 Ejercicio 3 — Clasificación de números (for + continue)

    
    numeros = [15, 0, -8, 22, 13, -3]
    
    positivos = []
    negativos = []
    
    for n in numeros:
        if n == 0:
            continue  # omitimos el cero
        if n > 0:
            positivos.append(n)
        else:
            negativos.append(n)
    
    print("Positivos:", positivos)
    print("Negativos:", negativos)
      

    Conceptos: continue, control de flujo, clasificación y comprensión lógica.


    📊 Ejercicio 4 — Imprimir matriz con índices (enumerate() + anidado)

    
    matriz = [
        [10, 20, 30],
        [40, 50, 60],
        [70, 80, 90]
    ]
    
    for i, fila in enumerate(matriz, start=1):
        for j, valor in enumerate(fila, start=1):
            print(f"Fila {i}, Columna {j} → {valor}")
      

    💡 Perfecto para representar datos tabulares o manejar posiciones en matrices 2D.


    ⚙️ Ejercicio 5 — Contador con iter() y next()

    
    datos = ["Inicio", "Carga", "Procesamiento", "Fin"]
    it = iter(datos)
    
    while True:
        try:
            etapa = next(it)
            print("→", etapa)
        except StopIteration:
            print("✅ Proceso completo.")
            break
      

    Conceptos: iteradores manuales y control con excepciones.


    🧠 Ejercicio 6 — Menú interactivo (while + match)

    
    while True:
        print("""
        === MENÚ ===
        1. Mostrar saludo
        2. Mostrar cuenta regresiva
        3. Salir
        """)
    
        opcion = input("Elige opción: ")
    
        match opcion:
            case "1":
                print("👋 ¡Hola, mundo Python!")
            case "2":
                for n in range(5, 0, -1):
                    print(n)
            case "3":
                print("👋 Saliendo del programa...")
                break
            case _:
                print("❌ Opción no válida")
      

    💡 Este patrón combina iteración + control de flujo + condiciones múltiples.


    📉 Rendimiento en bucles

    Python es rápido en iteraciones pequeñas, pero en colecciones grandes debes optimizar.
    Aquí van algunos consejos:

    • 🚀 Usa comprensiones ([x*x for x in range(1000)]) en lugar de bucles explícitos cuando sea posible.
    • 📦 Evita operaciones costosas dentro del bucle (p. ej. llamadas repetidas a funciones).
    • ⚙️ Usa generadores ((x*x for x in datos)) para ahorrar memoria.
    • 💾 Precalcula valores que no cambian dentro del ciclo.

    Ejemplo: diferencia de eficiencia

    
    # 👎 Inneficiente: accede muchas veces al método len()
    for i in range(len(lista)):
        if len(lista) > 100:
            ...
    
    # 👍 Eficiente
    n = len(lista)
    for i in range(n):
        if n > 100:
            ...
      

    📈 Ejercicio 7 — Comparación de rendimiento

    Usa el módulo time para medir dos estrategias distintas de recorrido.

    
    import time
    
    datos = list(range(1_000_000))
    
    inicio = time.time()
    total = 0
    for n in datos:
        total += n
    print("FOR:", time.time() - inicio)
    
    inicio = time.time()
    total = sum(datos)
    print("BUILT-IN SUM:", time.time() - inicio)
      

    💡 Conclusión: las funciones nativas como sum(), min() o any() están optimizadas en C y son más rápidas que los bucles Python puros.


    🧮 Ejercicio 8 — Procesar datos con zip() y filtrado

    Supón que tienes dos listas: productos y precios.
    Crea un nuevo listado solo con los productos cuyo precio supere los 10 €.

    
    productos = ["Pan", "Leche", "Queso", "Agua"]
    precios = [2, 1.5, 12, 0.9]
    
    caros = [(p, pr) for p, pr in zip(productos, precios) if pr > 10]
    
    print("Productos caros:")
    for p, pr in caros:
        print(f"- {p}: {pr} €")
      

    ✅ Combina zip(), listas por comprensión y condición en una sola línea legible.


    🧾 Autoevaluación final

    • ✅ Entiendo cómo y cuándo usar for y while.
    • ✅ Puedo usar break, continue y pass para controlar el flujo.
    • ✅ Sé trabajar con bucles anidados.
    • ✅ Sé aplicar enumerate(), zip() e iter() correctamente.
    • ✅ Conozco cómo escribir bucles eficientes y legibles.

    📚 Resumen visual

    TemaResumenEjemplo
    forRecorre secuencias o coleccionesfor x in lista:
    whileRepite mientras se cumpla una condiciónwhile x < 10:
    breakRompe el bucle actualif x == 5: break
    continueSalta a la siguiente iteraciónif x % 2 == 0: continue
    zip()Combina listas en paralelofor a,b in zip(x,y):
    enumerate()Añade índices automáticosfor i,v in enumerate(lista):
    iter()Crea iteradores manualesit = iter(lista)

    💬 Reflexión final

    Dominar las estructuras de iteración es dominar el pulso lógico del programa.
    Los bucles son la esencia de toda automatización: repiten, filtran, procesan y generan resultados.
    La clave está en mantener claridad y eficiencia:

    • Menos líneas ≠ mejor código; legibilidad primero.
    • Aprende a usar funciones nativas y comprensión de listas para simplificar tareas.
    • Optimiza los bucles grandes con generadores e iteradores.

    ➡️ En la próxima lección entrarás en el tema Funciones y Manejo de Excepciones y errores,
    donde aprenderás a encapsular código repetitivo dentro de bloques reutilizables, llevando tu dominio de Python al siguiente nivel.

    Nuestra puntuación
    ¡Haz clic para puntuar esta entrada!
    (Votos: 0 Promedio: 0)
    Scroll al inicio