Categoría: Programación

  • 1.5 – ¿Qué es una tupla?

    1.5 – ¿Qué es una tupla?

    Las tuplas son criaturas curiosas del ecosistema Python. Parecen listas… pero no lo son. Son inmutables, compactas y muy seguras. Cuando algo en tu programa no debe cambiar jamás, una tupla es la cápsula criogénica perfecta.


    Una tupla (tuple) es una colección ordenada e inmutable de elementos.

    • Ordenada → mantiene el orden de inserción
    • Inmutable → no se puede modificar tras crearla
    • Permite duplicados
    • Puede contener distintos tipos

    La inmutabilidad las hace más rápidas, más seguras y más ligeras que las listas.


    Crear una tupla

    mi_tupla = (1, 2, 3, 4, 5)

    También se puede crear sin paréntesis:

    mi_tupla = 1, 2, 3, 4, 5

    Tupla vacía:

    vacia = ()

    Tuplas de un solo elemento

    Detalle clásico que rompe cerebros:

    tupla = (42,)   # Correcto
    no_tupla = (42) # Es un entero, NO una tupla

    La coma es obligatoria.


    Acceso a elementos

    Por índice

    t = (10, 20, 30, 40)
    print(t[0])    # 10
    print(t[-1])   # 40

    Slicing (rebanado)

    t = (10, 20, 30, 40, 50)
    print(t[1:4])   # (20, 30, 40)
    print(t[:3])    # (10, 20, 30)
    print(t[::2])   # (10, 30, 50)

    El slicing devuelve otra tupla.


    Métodos de las tuplas

    Las tuplas tienen pocos métodos porque no se pueden modificar.

    count(x)

    Cuenta cuántas veces aparece un valor.

    t = (1,2,3,2,2)
    print(t.count(2)) # 3

    index(x)

    Devuelve la posición del primer valor encontrado.

    t = (10,20,30,40)
    print(t.index(30)) # 2

    Si no existe → error.


    Funciones útiles con tuplas

    t = (10,20,30,40)
    len(t)   # tamaño
    min(t)   # mínimo
    max(t)   # máximo
    sum(t)   # suma (si numéricos)

    Concatenar y repetir tuplas

    Concatenar

    t1 = (1,2,3)
    t2 = (4,5,6)t3 = t1 + t2
    print(t3)

    Repetir

    t = (1,2,3)
    print(t * 2)

    Convertir entre lista y tupla

    Las tuplas no se pueden modificar, pero puedes convertirlas:

    t = (10,20,30)lista = list(t)
    lista.append(40)t = tuple(lista)
    print(t)

    Este es el truco clásico para “modificar” tuplas.


    Desempaquetado de tuplas

    Una de sus habilidades más elegantes.

    t = (10, 20, 30)a, b, c = t
    print(a, b, c)

    Desempaquetado extendido

    t = (1,2,3,4,5)a, *medio, b = t
    print(a) # 1
    print(medio) # [2,3,4]
    print(b) # 5

    Python convierte automáticamente el resto en lista.


    Verificar pertenencia

    t = (10,20,30)print(20 in t)   # True
    print(99 in t) # False

    Recorrer tuplas

    for valor in (1,2,3):
    print(valor)

    Con índice:

    t = (10,20,30)for i, v in enumerate(t):
    print(i, v)

    Por qué usar tuplas

    Usa tuplas cuando:

    • Los datos no deben cambiar
    • Quieres seguridad frente a modificaciones accidentales
    • Necesitas mejor rendimiento
    • Quieres usar datos como claves en diccionarios (las listas no pueden)

    Ejemplo real:

    coordenada = (40.4168, -3.7038)

    Diferencia clave: lista vs tupla

    CaracterísticaListaTupla
    MutableNo
    VelocidadMás lentaMás rápida
    MemoriaMásMenos
    MétodosMuchosPocos
    SeguridadBajaAlta

    Errores comunes

    Intentar modificar una tupla:

    t = (1,2,3)
    t[0] = 9 # TypeError

    Olvidar la coma en tupla de un elemento.


    Ejemplo práctico

    mi_tupla = (10, 20, 30, 40, 50)print(mi_tupla[1])
    print(mi_tupla.index(30))
    print(mi_tupla.count(20))lista = list(mi_tupla)
    lista.append(60)mi_tupla = tuple(lista)
    print(mi_tupla)

    Salida:

    (10, 20, 30, 40, 50, 60)

    Métodos principales de las tuplas en Python

    MétodoQué haceEjemploResultado
    count(x)Cuenta cuántas veces aparece un valor(1,2,2,3).count(2)2
    index(x)Devuelve el índice del primer valor encontrado(10,20,30).index(20)1

    Funciones útiles con tuplas

    (No son métodos propios de la tupla, pero se usan constantemente)

    FunciónQué haceEjemploResultado
    len(t)Número de elementoslen((1,2,3))3
    min(t)Valor mínimomin((5,2,9))2
    max(t)Valor máximomax((5,2,9))9
    sum(t)Suma de elementos numéricossum((1,2,3))6
    sorted(t)Devuelve lista ordenadasorted((3,1,2))[1,2,3]
    tuple(iterable)Convierte iterable a tuplatuple([1,2,3])(1,2,3)
    list(t)Convierte tupla a listalist((1,2,3))[1,2,3]

    Operaciones comunes con tuplas

    OperaciónQué haceEjemploResultado
    t1 + t2Concatenar tuplas(1,2) + (3,4)(1,2,3,4)
    t * nRepetir tupla(1,2) * 2(1,2,1,2)
    x in tVerificar pertenencia2 in (1,2,3)True
    x not in tVerificar ausencia9 in (1,2,3)False

    Recordatorio importante

    Las tuplas no tienen métodos para modificar datos porque son inmutables:

    • No append
    • No remove
    • No insert
    • No pop
    • No sort

    Si necesitas modificar → convierte a lista, modifica, y vuelve a tupla.


    Ejercicios

    1. Crea una tupla con 5 números y muestra el primero y último.
    2. Cuenta cuántas veces aparece un número repetido.
    3. Convierte una tupla a lista, añade un valor y vuelve a tupla.
    4. Usa desempaquetado para guardar los valores en variables.
    5. Crea una tupla de coordenadas y verifica si contiene cierto valor.
  • 1.6 – ¿Qué es un conjunto? (set)

    1.6 – ¿Qué es un conjunto? (set)

    Un set es una colección desordenada, mutable y sin elementos duplicados.

    • No mantiene orden
    • No permite duplicados
    • Es mutable
    • Permite operaciones matemáticas (unión, intersección, diferencia)
    • Muy eficiente en búsquedas

    Crear un conjunto

    mi_conjunto = {1, 2, 3, 4, 5}

    También usando el constructor:

    mi_conjunto = set([1, 2, 3, 4, 5])

    Conjunto vacío

    {} crea un diccionario, no un set.

    conjunto_vacio = set()

    Características importantes

    • No tienen índices
    • No permiten slicing
    • No mantienen orden
    • Solo aceptan elementos inmutables (no listas, no diccionarios)
    # Esto da error
    mi_set = {[1,2,3]}

    Métodos principales

    Agregar elementos

    s = {1,2,3}
    s.add(4)

    Eliminar elementos

    remove(x)

    Error si no existe.

    s.remove(2)

    discard(x)

    No error si no existe.

    s.discard(10)

    pop()

    Elimina elemento aleatorio.

    s.pop()

    clear()

    s.clear()

    Operaciones matemáticas de conjuntos

    Sean:

    a = {1,2,3}
    b = {3,4,5}

    Unión (elementos de ambos)

    a | b
    a.union(b)

    Resultado → {1,2,3,4,5}


    Intersección (elementos comunes)

    a & b
    a.intersection(b)

    Resultado → {3}


    Diferencia (elementos de A que no están en B)

    a - b
    a.difference(b)

    Resultado → {1,2}


    Diferencia simétrica (elementos que NO son comunes)

    a ^ b
    a.symmetric_difference(b)

    Resultado → {1,2,4,5}


    Relaciones entre conjuntos

    Subconjunto

    {1,2}.issubset({1,2,3})

    Superconjunto

    {1,2,3}.issuperset({1,2})

    Sin elementos en común

    {1,2}.isdisjoint({3,4})

    Comprobar pertenencia

    3 in {1,2,3}
    10 in {1,2,3}

    Esta operación es muy rápida (O(1)).


    Convertir entre estructuras

    Lista → Set (eliminar duplicados)

    lista = [1,2,2,3,4,4]
    sin_dup = set(lista)

    Set → Lista

    lista = list(sin_dup)

    Copiar conjuntos

    copia = mi_conjunto.copy()

    Recorrer conjuntos

    for elemento in {1,2,3}:
    print(elemento)

    El orden puede cambiar.


    Ejemplo práctico

    conjunto1 = {1,2,3,4}
    conjunto2 = {3,4,5,6}print(conjunto1 | conjunto2)
    print(conjunto1 & conjunto2)
    print(conjunto1 - conjunto2)
    print(conjunto1 ^ conjunto2)conjunto1.add(7)
    conjunto1.discard(2)print(conjunto1)

    Cuándo usar conjuntos

    Usa sets cuando:

    • Necesites eliminar duplicados
    • Quieras operaciones matemáticas
    • Necesites búsquedas rápidas
    • No importe el orden

    Errores comunes

    Intentar indexar un set:

    s = {1,2,3}
    print(s[0]) # Error

    Intentar meter listas o diccionarios dentro.


    Tabla de métodos principales

    MétodoQué haceEjemploResultado
    add(x)Añade elemento{1,2}.add(3){1,2,3}
    remove(x)Elimina (error si no existe){1,2}.remove(2){1}
    discard(x)Elimina sin error{1,2}.discard(5){1,2}
    pop()Elimina elemento aleatorio{1,2}.pop(){2} o {1}
    clear()Vacía el conjuntos.clear()set()
    union()Unióna.union(b){1,2,3,4,5}
    intersection()Interseccióna.intersection(b){3}
    difference()Diferenciaa.difference(b){1,2}
    symmetric_difference()Diferencia simétricaa.symmetric_difference(b){1,2,4,5}
    issubset()Subconjunto{1}.issubset({1,2})True
    issuperset()Superconjunto{1,2}.issuperset({1})True
    isdisjoint()Sin comunes{1}.isdisjoint({2})True
    copy()Copia superficials.copy()nuevo set

    Ejercicios

    1. Elimina duplicados de una lista usando set.
    2. Calcula intersección entre dos listas convertidas a set.
    3. Comprueba si un conjunto es subconjunto de otro.
    4. Crea dos sets y calcula diferencia simétrica.
    5. Cuenta elementos únicos en una lista grande.
  • 1.7 – ¿Qué es un diccionario?

    1.7 – ¿Qué es un diccionario?

    Un diccionario (dict) es una colección de pares clave → valor.

    • Las claves son únicas
    • Son mutables
    • Permiten acceso rápido por clave
    • Desde Python 3.7 mantienen el orden de inserción

    Un diccionario es como una base de datos en miniatura.


    Crear un diccionario

    mi_diccionario = {
    "nombre": "Juan",
    "edad": 25,
    "ciudad": "Madrid"
    }

    También se puede crear así:

    mi_diccionario = dict(nombre="Juan", edad=25)

    Diccionario vacío

    diccionario_vacio = {}

    Características importantes

    • Las claves deben ser inmutables (str, int, tuple…)
    • No pueden existir claves duplicadas
    • El acceso es muy rápido (O(1))

    Esto es inválido:

    mi_dic = {[1,2,3]: "error"}  # Las listas no pueden ser clave

    Acceso a elementos

    Acceso directo

    nombre = mi_diccionario["nombre"]

    Si la clave no existe → error.


    Usar get()

    profesion = mi_diccionario.get("profesion", "No especificada")

    Evita errores si la clave no existe.


    Modificar o agregar elementos

    mi_diccionario["edad"] = 26
    mi_diccionario["profesion"] = "Ingeniero"

    Si la clave existe → modifica.
    Si no existe → crea.


    Eliminar elementos

    pop()

    edad = mi_diccionario.pop("edad")

    del

    del mi_diccionario["ciudad"]

    popitem()

    Elimina el último elemento añadido.

    ultimo = mi_diccionario.popitem()

    clear()

    mi_diccionario.clear()

    Consultar claves, valores y pares

    claves = mi_diccionario.keys()
    valores = mi_diccionario.values()
    items = mi_diccionario.items()

    Devuelven objetos tipo “vista”.


    Verificar si una clave existe

    "nombre" in mi_diccionario

    Copiar diccionarios

    copia = mi_diccionario.copy()

    O:

    copia = dict(mi_diccionario)

    Mezclar diccionarios

    update()

    otro = {"pais": "España", "edad": 30}
    mi_diccionario.update(otro)

    Sobrescribe si la clave ya existe.


    Operador moderno (Python 3.9+)

    nuevo = mi_diccionario | otro

    Longitud

    len(mi_diccionario)

    Iterar sobre diccionarios

    Solo claves

    for clave in mi_diccionario:
    print(clave)

    Solo valores

    for valor in mi_diccionario.values():
    print(valor)

    Clave y valor

    for clave, valor in mi_diccionario.items():
    print(clave, valor)

    Diccionarios anidados

    usuario = {
    "nombre": "Ana",
    "direccion": {
    "ciudad": "Barcelona",
    "codigo_postal": 08001
    }
    }print(usuario["direccion"]["ciudad"])

    Comprensión de diccionarios

    cuadrados = {x: x**2 for x in range(5)}
    print(cuadrados)

    Ejemplo práctico completo

    mi_diccionario = {
    "nombre": "Ana",
    "edad": 28,
    "ciudad": "Barcelona"
    }print(mi_diccionario["nombre"])mi_diccionario["edad"] = 29
    mi_diccionario["profesion"] = "Diseñadora"mi_diccionario.pop("ciudad")for clave, valor in mi_diccionario.items():
    print(f"{clave}: {valor}")

    Tabla resumen de métodos principales

    MétodoQué haceEjemploResultado
    get()Obtiene valor sin errord.get("x")valor o None
    pop()Elimina y devuelve valord.pop("edad")29
    popitem()Elimina último pard.popitem()(«clave», valor)
    clear()Vacía diccionariod.clear(){}
    keys()Devuelve clavesd.keys()vista de claves
    values()Devuelve valoresd.values()vista de valores
    items()Devuelve paresd.items()vista de tuplas
    update()Mezcla diccionariosd.update(otro)dic actualizado
    copy()Copia superficiald.copy()nuevo dict

    Cuándo usar diccionarios

    Usa diccionarios cuando:

    • Necesites acceso rápido por clave
    • Quieras representar datos estructurados
    • Trabajes con JSON
    • Simules registros o entidades

    Errores comunes

    • Intentar acceder a clave inexistente sin usar get()
    • Usar listas como claves
    • Confundir claves con valores
  • 1.8 – Bucles

    1.8 – Bucles

    Un bucle es una estructura que repite un bloque de código.

    En Python, usamos bucles para:

    • recorrer colecciones (listas, tuplas, sets, diccionarios, strings)
    • repetir acciones varias veces
    • ejecutar código hasta que se cumpla una condición
    • buscar elementos
    • construir resultados (sumas, contadores, filtros, etc.)

    Los dos bucles principales en Python son:

    • for → cuando recorres una secuencia/iterable o sabes “cuántas vueltas”
    • while → cuando repites mientras una condición sea verdadera (número de vueltas “desconocido”)

    1) Bucle for

    for recorre un iterable (lista, tupla, cadena, rango, diccionario, etc.)

    Recorrer una lista

    for elemento in [1, 2, 3, 4]:
    print(elemento)

    Recorrer un rango

    for i in range(5):   # 0,1,2,3,4
    print(i)

    Recorrer una cadena

    palabra = "Python"
    for letra in palabra:
    print(letra)

    1.1) range()

    range() genera números sin crear una lista enorme en memoria.

    • range(fin) → 0 … fin-1
    • range(inicio, fin) → inicio … fin-1
    • range(inicio, fin, paso) → saltos controlados
    for i in range(1, 11):
    print(i) # 1..10
    for i in range(10, 0, -1):
    print(i) # 10..1

    1.2) Recorrer con índice: enumerate()

    Cuando necesitas índice + valor, enumerate() es la forma correcta.

    nombres = ["Ana", "Luis", "Carlos"]for indice, nombre in enumerate(nombres):
    print(indice, nombre)

    Empezar desde 1 (muy útil para menús):

    for i, nombre in enumerate(nombres, start=1):
    print(i, nombre)

    1.3) Recorrer varias listas a la vez: zip()

    nombres = ["Ana", "Luis", "Carlos"]
    edades = [20, 25, 30]for nombre, edad in zip(nombres, edades):
    print(nombre, edad)

    2) Bucle while

    while repite mientras la condición sea verdadera.

    Contador simple

    contador = 0
    while contador < 5:
    print(contador)
    contador += 1

    Se usa mucho en:

    • validación de entrada (pedir hasta que sea correcto)
    • menús
    • bucles “hasta encontrar algo”
    • juegos (adivina el número)
    • lectura de datos hasta fin

    3) Control del bucle: break, continue, pass

    break (romper el bucle)

    for n in range(10):
    if n == 5:
    break
    print(n)

    continue (saltar a la siguiente vuelta)

    for n in range(10):
    if n % 2 != 0:
    continue
    print(n) # solo pares

    pass (no hacer nada, placeholder)

    for n in range(3):
    pass # "todavía no he programado esto"

    4) El else en bucles (truco ninja de Python)

    Un bucle puede tener else.
    Se ejecuta solo si NO se ha hecho break.

    Buscar un elemento

    objetivo = 7for n in [1, 3, 5, 7, 9]:
    if n == objetivo:
    print("Encontrado")
    break
    else:
    print("No encontrado")

    5) Bucles anidados

    Un bucle dentro de otro. Se usa en:

    • tablas (filas/columnas)
    • matrices
    • combinaciones
    for i in range(3):
    for j in range(2):
    print(f"i={i}, j={j}")

    Tabla de multiplicar

    numero = 5
    for i in range(1, 11):
    print(f"{numero} x {i} = {numero*i}")

    6) Iteradores: iter() y next()

    Puedes recorrer manualmente un iterable con un iterador.

    frutas = ["manzana", "banana", "cereza"]
    it = iter(frutas)print(next(it)) # manzana
    print(next(it)) # banana
    print(next(it)) # cereza

    Si te pasas, lanza StopIteration.


    7) Operadores relacionados: in y not in

    frutas = ["manzana", "banana", "cereza"]
    if "pera" not in frutas:
        print("La pera no está en la lista")

    8) Errores comunes y buenas prácticas

    Error típico: bucle infinito con while

    Si no actualizas la condición:

    # cuidado: infinito
    # while True:
    # print("hola")

    Buena práctica: evitar recorrer con índices si no hace falta

    Mejor:

    for fruta in frutas:
    print(fruta)

    En vez de:

    for i in range(len(frutas)):
    print(frutas[i])

    9) Ejemplos guiados

    Sumar lista de números

    numeros = [1, 2, 3, 4, 5]
    suma = 0for n in numeros:
    suma += nprint(suma)

    Filtrar pares

    numeros = [10, 15, 20, 25, 30]
    pares = []for n in numeros:
    if n % 2 == 0:
    pares.append(n)print(pares)

    Recorrer diccionario

    productos = {"manzana": 1.5, "banana": 0.8, "leche": 2.3}for producto, precio in productos.items():
    print(producto, precio)

    10) Ejercicios

    Bloque 1: Iterar sobre listas

    1. Dada una lista de números, calcula la suma.

    numeros = [1, 2, 3, 4, 5]

    2. Dada una lista de nombres, imprime cada nombre en mayúsculas.
    nombres = [«ana», «luis», «carlos»]

    1. Crea una lista con solo los pares.

    numeros = [10, 15, 20, 25, 30]

    Bloque 2: Iterar sobre diccionarios

    1. Imprime producto y precio.
    productos = {«manzana»: 1.5, «banana»: 0.8, «leche»: 2.3}

    1. Suma total de precios.
    2. Lista de productos cuyo precio sea mayor que 1.0.

    Bloque 3: Iterar sobre cadenas

    1. Cuenta vocales en una cadena.

    cadena = «Python es genial»

    2. Invierte una cadena con un bucle.

    cadena = «Hola Mundo»

    3. Elimina espacios y genera una nueva.

    cadena = «Hola a todos»

    Bloque 4: Índices (`range` / `enumerate`)

    1. Imprime del 1 al 10 con `range`.

    2. Tabla del 5.
    numero = 5

    3. Con enumerate, imprime índice y valor.

    frutas = [«manzana», «banana», «pera»]

    Bloque 5: `while`

    1. Imprime del 1 al 10.

    2. Pide números hasta que introduzca 0 y suma todo.

    3. Factorial con while.
    numero = 5


    Bloque 6: bucle infinito con break

    1. Contraseña correcta hasta acertar.

    contrasena_correcta = «python123»

    2. Genera aleatorios 1-10 hasta que salga > 8.
    import random

    3. Adivina número 1-20 o escribe «salir».

  • 1.9 – Patrones mentales de los bucles

    1.9 – Patrones mentales de los bucles

    1. Patrón ACUMULADOR (sumar / combinar resultados)

    Se usa cuando quieres acumular un resultado progresivamente.

    Ejemplos típicos:

    • Sumar números
    • Calcular media
    • Concatenar texto
    • Multiplicar valores

    Ejemplo: suma de una lista

    numeros = [10, 20, 30, 40]
    suma = 0   # ← acumulador
    for n in numeros:
        suma += nprint(suma)

    Idea mental:

    resultado = valor_inicial
    repetir:
    resultado = resultado + algo

    2. Patrón CONTADOR (contar ocurrencias)

    Se usa cuando quieres contar cuántas veces ocurre algo.

    Ejemplos:

    • Contar pares
    • Contar vocales
    • Contar aprobados

    Ejemplo: contar pares

    numeros = [1,2,3,4,5,6]
    contador = 0
    for n in numeros:
        if n % 2 == 0:
            contador += 1print(contador)

    Idea mental:

    contador = 0
    si ocurre algo → contador++

    3. Patrón CENTINELA (parar cuando ocurre algo)

    Se usa cuando no sabes cuántas iteraciones habrá y paras cuando aparece una condición especial.

    Muy común con while.

    Ejemplos:

    • Leer hasta introducir 0
    • Leer hasta EOF
    • Menús interactivos
    • Contraseña correcta

    Ejemplo: sumar hasta que el usuario introduzca 0

    suma = 0
    while True:
        n = int(input("Número (0 para salir): "))
        if n == 0:
            break
        suma += nprint(suma)

    Idea mental:

    repetir hasta que aparezca señal_de_parada

    La señal = centinela.


    4. Patrón BÚSQUEDA (encontrar algo)

    Se usa cuando quieres localizar un elemento.

    Ejemplos:

    • Buscar número en lista
    • Buscar palabra en texto
    • Buscar usuario en base de datos

    Ejemplo: buscar un número

    numeros = [5, 8, 3, 9, 2]
    objetivo = 9
    encontrado = False
    for n in numeros:
        if n == objetivo:
            encontrado = True
            breakprint(encontrado)

    Idea mental:

    encontrado = False
    recorrer:
    si coincide → parar

    5. Patrón FILTRADO (seleccionar algunos)

    Se usa cuando quieres quedarte solo con elementos que cumplen condición.

    Ejemplos:

    • Solo pares
    • Solo mayores de edad
    • Solo palabras largas

    Ejemplo: filtrar pares

    numeros = [1,2,3,4,5,6]
    pares = []
    for n in numeros ():
        if n % 2 == 0:
            pares.append(n)print(pares)

    Idea mental:

    resultado = []
    si cumple condición → guardar

    6. Patrón CONSTRUCCIÓN (generar algo nuevo)

    Se usa cuando quieres crear una nueva estructura a partir de otra.

    Ejemplos:

    • Crear lista de cuadrados
    • Convertir texto a lista
    • Transformar datos

    Ejemplo: cuadrados

    numeros = [1,2,3,4]
    cuadrados = []for n in numeros:
    cuadrados.append(n**2)print(cuadrados)

    Versión avanzada (list comprehension):

    cuadrados = [n**2 for n in numeros]

    Idea mental:

    resultado = []
    por cada elemento:
        transformar
        guardar

    Resumen mental

    PatrónPregunta mental
    Acumulador¿Estoy combinando resultados?
    Contador¿Estoy contando ocurrencias?
    Centinela¿Debo parar cuando pase algo?
    Búsqueda¿Estoy intentando encontrar algo?
    Filtrado¿Solo quiero algunos elementos?
    Construcción¿Estoy creando una nueva colección?

    Ejercicio de pensamiento (muy potente)

    Para cada problema, identifica primero el patrón:

    1. Sumar precios de una lista → Acumulador
    2. Contar aprobados → Contador
    3. Pedir contraseña hasta acertar → Centinela
    4. Buscar usuario → Búsqueda
    5. Quedarse con números > 10 → Filtrado
    6. Crear lista de cuadrados → Construcción
  • 1.12 – Batería de ejercicios (Con soluciones)

    1.12 – Batería de ejercicios (Con soluciones)

    Este conjunto de ejercicios en Python está organizado por bloques temáticos, y cubre los fundamentos del lenguaje de forma progresiva. Se incluyen:

    Impresión y variables: primeros pasos con print(), declaración de variables, tipos básicos (enteros, float, booleanos, cadenas).

    Funciones: creación de funciones con parámetros y valores de retorno, uso de funciones para modularizar código.

    Condicionales: uso de ifelif y else para tomar decisiones en base a condiciones.

    Bucles: implementación de bucles for y while para repetición de instrucciones.

    Colecciones: manejo de listas, tuplas, conjuntos y diccionarios. Acceso, modificación y recorrido.

    Archivos: lectura y escritura de archivos de texto utilizando open(), y comprensión básica de flujos de datos.

    Entrada de datos: uso de input() para interactuar con el usuario.

    Estos ejercicios están diseñados para estudiantes que se están iniciando en la programación, y cada script representa un problema o concepto básico resuelto de manera clara y concisa.

    Variables

    Ejercicio 1: Crea tres variables: una que guarde tu nombre, otra tu edad, y otra tu ciudad. Imprime los valores con un mensaje como:
    «Hola, mi nombre es [nombre], tengo [edad] años y vivo en [ciudad].»

    Ejercicio 2: Declara una variable con tu edad. Luego usa esta variable para calcular cuántos días, horas y minutos has vivido (aproximadamente).

        Ejemplo:
        Días: edad * 365
        Horas: días * 24
        Minutos: horas * 60

    Ejercicio 3: Escribe un programa que pida al usuario su nombre y su comida favorita. Luego imprime un mensaje como:
    «Hola [nombre], me alegra saber que te gusta [comida favorita].»

    Ejercicio 4: Declara una variable de cada tipo:

    • Un número entero (int), por ejemplo, 42.
    • Un número decimal (float), por ejemplo, 3.14.
    • Una cadena de texto (str), por ejemplo, «Hola».
    • Un valor booleano (bool), por ejemplo, True.
    • Imprime el valor y el tipo de cada variable con type().

    Condicionales

    Bloque 1: Introducción al uso de if

    1. Ejercicio 1:
      Pide al usuario un número. Si el número es mayor que 10, imprime:
      "El número es mayor que 10."
      Si no, no hagas nada.
    2. Ejercicio 2:
      Pide al usuario su edad. Si tiene 18 años o más, imprime:
      "Eres mayor de edad."
      Si tiene menos de 18, imprime:
      "Eres menor de edad."
    3. Ejercicio 3:
      Declara una variable con un número cualquiera. Si el número es positivo, imprime:
      "El número es positivo."
      Si el número es negativo, imprime:
      "El número es negativo."
      Si el número es 0, imprime:
      "El número es cero."

    Bloque 2: Operadores Comparativos

    1. Ejercicio 4:
      Pide al usuario dos números. Usa condicionales para imprimir:
      • "El primer número es mayor que el segundo."
      • "El segundo número es mayor que el primero."
      • "Ambos números son iguales."
    2. Ejercicio 5:
      Pide al usuario un número. Si el número está entre 1 y 100 (inclusive), imprime:
      "El número está dentro del rango."
      Si no, imprime:
      "El número está fuera del rango."

    Bloque 3: Uso de Condiciones Anidadas

    1. Ejercicio 6:
      Pide al usuario una letra. Si la letra es una vocal (a, e, i, o, u), imprime:
      "Es una vocal."
      Si no, imprime:
      "No es una vocal."
    2. Ejercicio 7:
      Pide al usuario un número del 1 al 7 y muestra el día de la semana correspondiente:
      • 1: Lunes
      • 2: Martes
      • 7: Domingo
        Si el número no está entre 1 y 7, imprime:
        "Número fuera de rango."

    Bloque 4: Problemas de Aplicación

    1. Ejercicio 8:
      Pide al usuario una contraseña y verifica si es igual a "secreto123". Si es correcta, imprime:
      "Acceso concedido."
      Si no, imprime:
      "Contraseña incorrecta."
    2. Ejercicio 9:
      Pide al usuario una temperatura en grados Celsius. Si la temperatura es:
      • Mayor o igual a 30: imprime "Hace calor."
      • Entre 10 y 29: imprime "El clima es templado."
      • Menor a 10: imprime "Hace frío."
    3. Ejercicio 10:
      Pide al usuario el precio de un producto. Si el precio es mayor a 1000, aplica un descuento del 10% y muestra el precio final. Si no, muestra el precio sin descuento.

    Bucles


    Bloque 1: Iterar sobre listas

    1. Dada una lista de números, calcula la suma de todos los elementos.numeros = [1, 2, 3, 4, 5]
    2. Dada una lista de nombres, imprime cada nombre en mayúsculas.nombres = ["ana", "luis", "carlos"]
    3. Crea una nueva lista que contenga solo los números pares de una lista dada.numeros = [10, 15, 20, 25, 30]

    Bloque 2: Iterar sobre diccionarios

    1. Dado un diccionario de productos y precios, imprime cada producto junto con su precio.productos = {"manzana": 1.5, "banana": 0.8, "leche": 2.3}
    2. Encuentra la suma total de los precios de los productos.
    3. Crea una lista de productos cuyo precio sea mayor que un valor dado (por ejemplo, 1.0).

    Bloque 3: Iterar sobre cadenas

    1. Dada una cadena, cuenta cuántas vocales contiene.cadena = "Python es genial"
    2. Invierte una cadena usando un bucle.cadena = "Hola Mundo"
    3. Elimina los espacios de una cadena y genera una nueva sin ellos.cadena = "Hola a todos"

    Bloque 4: Iterar con índices (range y enumerate)

    1. Usa range para imprimir los números del 1 al 10.# Salida esperada: 1, 2, ..., 10
    2. Usa range para generar una tabla de multiplicar (por ejemplo, la tabla del 5).numero = 5
    3. Usa enumerate para imprimir el índice y el valor de cada elemento en una lista.frutas = ["manzana", "banana", "pera"]

    Bloque 5: Bucle controlado por condición while

    1. Imprime los números del 1 al 10 usando un bucle while.
    2. Solicita al usuario números hasta que introduzca un 0, luego calcula la suma de los números ingresados.
    3. Dado un número, encuentra su factorial usando un bucle while.numero = 5

    Bloque 6: Bucle infinito controlado con break

    1. Simula una contraseña: solicita al usuario que introduzca una contraseña correcta hasta que lo haga bien.contrasena_correcta = "python123"
    2. Genera números aleatorios entre 1 y 10 hasta que se genere un número mayor que 8.import random
    3. Crea un programa que permita al usuario adivinar un número secreto entre 1 y 20. Termina cuando el usuario lo adivine o escriba «salir».

    Funciones

    1. Ejercicio 1: Función de resta
      • Define una función llamada restar que acepte dos parámetros y devuelva su diferencia.
      • Prueba la función con diferentes valores.
    2. Ejercicio 2: Función de saludo personalizado
      • Define una función llamada saludo_personalizado que acepte dos parámetros: nombre y edad.
      • La función debe imprimir un mensaje como «Hola, [nombre]. Tienes [edad] años.»
      • Prueba la función con diferentes valores.
    3. Ejercicio 3: Función de multiplicación
      • Define una función llamada multiplicar que acepte dos parámetros y devuelva su producto.
      • Prueba la función con diferentes valores.
    4. Ejercicio 4: Función de número par o impar
      • Define una función llamada es_par que acepte un número y devuelva True si el número es par y False si es impar.
      • Prueba la función con diferentes valores.
    5. Ejercicio 5: Función con parámetros por defecto
      • Define una función llamada saludar_con_titulo que acepte dos parámetros: nombre y titulo, con un valor por defecto para titulo.
      • La función debe imprimir un mensaje como «Hola, [titulo] [nombre].»
      • Prueba la función con y sin el parámetro titulo.
    6. Ejercicio 6: Función con *args
      • Define una función llamada promedio que acepte una cantidad arbitraria de números y devuelva su promedio.
      • Prueba la función con diferentes cantidades de argumentos.
    7. Ejercicio 7: Función con **kwargs
      • Define una función llamada imprimir_detalles que acepte una cantidad arbitraria de parámetros con nombre y los imprima en formato clave-valor.
      • Prueba la función con diferentes conjuntos de parámetros.

    Trabajando con Archivos

    1. Lectura de contenido: Crea un programa que lea el contenido de un archivo llamado notas.txt y lo imprima en la consola.
    2. Contar líneas: Escribe un programa que cuente cuántas líneas tiene un archivo de texto llamado poema.txt.
    3. Buscar palabra: Diseña un programa que busque una palabra dada por el usuario dentro de un archivo llamado articulo.txt e indique cuántas veces aparece.
    4. Escritura de lista: Escribe un programa que tome una lista de nombres y los guarde en un archivo llamado nombres.txt, un nombre por línea.
    5. Copiar contenido: Implementa un programa que copie el contenido de un archivo llamado origen.txt a un archivo llamado destino.txt.

    Descarga solución de los ejercicios.

    En programamación un solución puede tener multiples soluciones, mas tratandose de un lenguaje tan dinámico como Python. En el siguieten archivo encotraras una de esas posibilidades, pero recuerda que no por eso tu planteamiento para resolverlo sería incorrecto.

  • App Web Full-Stack (PHP/MySQL + HTML/JS)

    App Web Full-Stack (PHP/MySQL + HTML/JS)

    Desarrollar una aplicación web con frontend (HTML/JS) separado de un backend (PHP en POO + MySQL) expuesto como Web Service (API REST). La app debe incluir sistema de sesión con login y al menos un CRUD completo. El proyecto se versionará en GitHub con un README detallado y aplicará medidas mínimas de seguridad.

    1) Alcance funcional mínimo

    Elige una entidad (ej.: biblioteca, inventario, reservas, incidencias, tareas, alumnos/cursos).
    Debe incluir, como mínimo:

    • 1 entidad principal con CRUD completo: Crear, Leer (lista + detalle), Actualizar, Eliminar.
    • Autenticación de usuarios con login/logout y sesión (no solo “mock”).
    • Autorización básica: acciones protegidas solo para usuarios autenticados.
    • Listado con búsqueda y/o filtrado en el frontend.
    • Validaciones en front y back.

    Sugerencia de entidades: users (para login) + una entidad de negocio como itemscoursesticketsbookings, etc.

    2) Requisitos técnicos

    • Backend
      • Lenguaje: PHP (>=7.4; recomendable 8.x).
      • Estilo: POO (clases para modelos, controladores, servicios/repositorios).
      • Acceso a datos: MySQL con prepared statements (PDO o MySQLi).
      • Rutas tipo REST: /api/... que devuelvan JSON.
      • Gestión de sesión  (PHP session) 
    • Frontend
      • HTML + CSS + JS (vanilla).
      • Consume la API vía fetch.
    • Separación front/back
      • epositorio con carpetas: /frontend y /backend.
    • Base de datos
      • Script db/schema.sql + db/seed.sql (opcional) para poblar datos de prueba.
    • GitHub
      • Uso de commits frecuentes, ramas (opcional) y README completo.
    • Documentación
      • README.md en raíz con: descripción, arquitectura, requisitos, instalación, variables de entorno, comandos, endpoints, roles, capturas/GIF, autores.

    3) Seguridad (mínimos obligatorios)

    • Passwords: almacenar con password_hash() / password_verify().
    • SQL InjectionPDO + prepared statements SIEMPRE.
    • Sesiones:
      • session.cookie_httponly = 1session.cookie_secure = 1 (si HTTPS), SameSite=Lax.
      • Regenerar ID de sesión tras login.
      • Tiempo de inactividad y expiración configurados.
    • Reglas de BD: usuario MySQL con mínimos privilegios (solo la base usada).
    • Errores: no exponer trazas en producción; logs internos.
    • Validación: back valida todo; front valida para UX, pero back es la autoridad
    • Backups: script/nota de backup de la BD (aunque sea manual).

     4) Entregables

    1. URL del repositorio GitHub (público o acceso concedido si privado).
    2. README.md completo.
    3. Script SQL (db/schema.sql + seed.sql opcional).
    4. Capturas o GIF corto del flujo principal (login + CRUD).
    5. Colección de Postman/Thunder Client (opcional, suma).
    6. Checklist de seguridad marcada (ver abajo).

    5) Plantilla de README.md (copiar/pegar)

    # Nombre del Proyecto

    Descripción corta del dominio elegido y objetivo de la app.

    ## Arquitectura
    – Frontend (HTML/JS) en `/frontend` consumiendo API REST JSON.
    – Backend (PHP OOP + MySQL) en `/backend` exponiendo `/api/…`.

    ## Requisitos
    – PHP 8.x, MySQL 5.7/8.x, servidor web (Apache/Nginx), Node (opcional).
    – Copiar `.env.example` a `.env` y completar valores.

    ## Instalación
    1. Clonar repo.
    2. Importar `db/schema.sql` (y `db/seed.sql` opcional).
    3. Configurar VirtualHost/servir `/backend/public` como raíz del back.
    4. Configurar `FRONT_ORIGIN` en `.env` y CORS.
    5. Abrir `/frontend/index.html` (o servir con dev server).

    ## Scripts útiles
    – (Opcional) `composer install`, `php -S localhost:8000 -t backend/public`, etc.

    ## Endpoints (resumen)
    – `POST /api/auth/login` — login (JSON body: email, password)
    – `POST /api/auth/logout` — logout
    – `GET /api/auth/me` — usuario autenticado
    – CRUD entidad principal: `/api/items`…

    ## Seguridad aplicada
    – Hashing, prepared statements, CSRF/SameSite, cabeceras, etc. (Checklist abajo)

    ## Capturas
    (Insertar imágenes o GIF)

    ## Checklist de Seguridad
    – [ ] …

  • Subida de imágenes en Java JEE usando Servlets (Multipart)

    Subida de imágenes en Java JEE usando Servlets (Multipart)

    En esta práctica aprenderás a:

    • Enviar un archivo desde un formulario HTML hacia un Servlet.
    • Procesar peticiones multipart/form-data.
    • Guardar una imagen en el servidor.
    • Obtener el nombre del archivo subido.
    • Persistir la referencia en base de datos.
    • Entender el flujo completo cliente → servidor → disco → BD → vista.

    Arquitectura del proceso

    El flujo que vamos a implementar es el siguiente:

    [Formulario HTML]
            ↓
    POST multipart/form-data
            ↓
    [Servlet controlador]
            ↓
    Procesa campos + fichero
            ↓
    Guarda imagen en disco
            ↓
    Inserta datos en BD (nombre del archivo)
            ↓
    Redirección a galería
            ↓
    [Vista HTML muestra la imagen]

    Video

    El Servlet

    Anotaciones necesarias

    @WebServlet("/BestiasFoto")
    @MultipartConfig
    public class BestiasFoto extends HttpServlet {

    @WebServlet

    Define la URL que ejecuta el servlet:

    http://servidor/app/BestiasFoto

    @MultipartConfig

    Activa el soporte de subida de archivos.

    Sin esta anotación:

    • request.getPart("foto") no funcionará
    • El servidor no podrá procesar multipart/form-data

    Variables del Servlet

    private String pathFiles = "C:\\entornos\\clasesMaster\\BestiarioJEE\\bestiario\\src\\main\\webapp\\img\\";
    private File uploads = new File(pathFiles);
    • pathFiles → carpeta donde se guardarán las imágenes
    • uploads → objeto File que representa esa carpeta

    En esta práctica guardamos las imágenes dentro del proyecto. En producción normalmente se usa una carpeta externa.


    Método doPost()

    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
    
        if(request.getParameter("opcion").equals("1")) {
            insertar_actualizarBestia(request,response);
        } else if(request.getParameter("opcion").equals("2")) {
            listarBestias(Integer.parseInt(request.getParameter("id")),request,response);
        }
    }

    Este método actúa como enrutador.

    opcionAcción
    1Inserta o actualiza una bestia + sube imagen
    2Listado de bestias

    Método insertar_actualizarBestia()

    Este método:

    1. Recoge los datos del formulario
    2. Sube la imagen
    3. Inserta en BD
    4. Redirige a la galería
    public void insertar_actualizarBestia(HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {
    
        int id = Integer.parseInt(request.getParameter("id"));
        String nombre = request.getParameter("nombre");
        int tam = Integer.parseInt(request.getParameter("tam"));
        int alineamiento = Integer.parseInt(request.getParameter("alineamiento"));
        String descripcion = request.getParameter("descripcion");
    
        String ruta = this.subirFoto(request, response);
    
        if(id != 0) {
            // actualizar
        } else {
            Bestia b = new Bestia(nombre, tam, alineamiento, descripcion, ruta);
            try {
                b.insertar();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            response.sendRedirect("galeria.html");
        }
    }

    Explicación

    • getParameter() → obtiene datos normales del formulario
    • subirFoto() → guarda la imagen y devuelve el nombre del archivo
    • sendRedirect() → evita reenviar el formulario al refrescar

    Método subirFoto()

    Este método recibe el archivo, lo guarda en el servidor y devuelve el nombre.

    private String subirFoto(HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {
    
        Part part = request.getPart("foto");
    
        Path path = Paths.get(part.getSubmittedFileName());
        String fileName = path.getFileName().toString();
    
        InputStream input = part.getInputStream();
    
        String ruta ="";
        String nombreArchivo ="";
    
        if(input != null) {
    
            File file = new File(uploads, fileName);
    
            ruta = file.getAbsolutePath();
            nombreArchivo = file.getName();
    
            System.out.println("La ruta de la foto es: " + ruta);
    
            try {
                Files.copy(input, file.toPath());
            } catch (Exception e) {
                PrintWriter respuesta = response.getWriter();
                respuesta.print("Error al copiar la foto");
                System.out.println("Error al copiar la foto");
            }
        }
    
        return nombreArchivo;
    }

    Qué ocurre paso a paso

    1. Obtener el archivo enviado

    Part part = request.getPart("foto");

    Part representa el archivo enviado desde el formulario.


    2. Obtener el nombre del archivo

    Path path = Paths.get(part.getSubmittedFileName());
    String fileName = path.getFileName().toString();

    Se usa Path para eliminar posibles rutas y quedarse solo con el nombre.


    3. Obtener los bytes del archivo

    InputStream input = part.getInputStream();

    Un InputStream permite leer el contenido del archivo.


    4. Crear el archivo destino

    File file = new File(uploads, fileName);

    Esto crea el archivo dentro de la carpeta img.


    5. Copiar el archivo

    Files.copy(input, file.toPath());

    Copia los bytes desde el InputStream al fichero del servidor.


    6. Devolver el nombre del archivo

    returnnombreArchivo;

    En la base de datos solo guardamos el nombre, no la ruta completa.


    Formulario HTML necesario

    <form action="BestiasFoto" method="post" enctype="multipart/form-data">
    
      <input type="hidden" name="opcion" value="1">
      <input type="hidden" name="id" value="0">
    
      <input type="text" name="nombre" placeholder="Nombre">
      <input type="number" name="tam" placeholder="Tamaño">
      <input type="number" name="alineamiento" placeholder="Alineamiento">
      <textarea name="descripcion"></textarea>
    
      <input type="file" name="foto" accept="image/*">
    
      <button type="submit">Guardar</button>
    
    </form>

    Mostrar la imagen guardada

    Si en BD se guarda dragon.jpg:

    <img src="img/dragon.jpg">

    Métodos utilizados

    HttpServletRequest

    MétodoFunción
    getParameter()Obtiene valores del formulario
    getPart()Obtiene el archivo subido

    Part

    MétodoFunción
    getSubmittedFileName()Nombre original del archivo
    getInputStream()Bytes del archivo
    getSize()Tamaño del archivo
    getContentType()Tipo MIME

    Files / Path

    MétodoFunción
    Files.copy()Copia el archivo al servidor
    Paths.get()Crea un Path
    getFileName()Obtiene el nombre del archivo

    Buenas prácticas

    • Guardar solo el nombre del archivo en BD
    • Validar que el archivo no esté vacío
    • Validar tipo MIME (image/png, image/jpeg…)
    • Evitar nombres duplicados
    • Crear la carpeta si no existe

    Tabla de métodos/clases usados

    Servlet / Request / Response

    ElementoMétodo¿Para qué sirve?
    HttpServletRequestgetParameter("x")Lee un campo normal del formulario (texto, hidden, number…)
    HttpServletRequestgetPart("foto")Recupera un archivo (o parte) del multipart con ese name
    HttpServletResponsesendRedirect("url")Redirige al navegador a otra página tras el POST
    HttpServletResponsegetWriter()Permite escribir una respuesta de texto al cliente

    Multipart / Part

    ElementoMétodo¿Para qué sirve?
    PartgetSubmittedFileName()Devuelve el nombre original del fichero enviado
    PartgetInputStream()Devuelve un stream con los bytes del archivo
    PartgetSize()Tamaño del archivo en bytes (útil para validar)
    PartgetContentType()Tipo MIME (ej. image/png)

    Files / Path

    ElementoMétodo¿Para qué sirve?
    Pathsget(String)Crea un Path a partir de una ruta o nombre
    PathgetFileName()Devuelve el último segmento (nombre)
    Filescopy(InputStream, Path, ...)Copia datos de un stream a un fichero
    FilegetAbsolutePath()Ruta absoluta en disco
    FilegetName()Nombre del archivo sin ruta

    Validaciones en la subida de imágenes (métodos independientes)

    • Validar el nombre del archivo
    • Evitar archivos duplicados
    • Validar tipo MIME
    • Validar extensión
    • Validar tamaño
    • Validar que el archivo sea realmente una imagen
    • Generar nombre seguro (UUID)

    Cada método tiene una responsabilidad única.


    1. Validar nombre de archivo

    Evitar nombres peligrosos, rutas maliciosas o caracteres inválidos.

    Método

    private String validarNombreArchivo(String originalName) throws ServletException {
    
        if (originalName == null || originalName.isBlank()) {
            throw new ServletException("El archivo no tiene nombre válido");
        }
    
        // eliminar rutas potenciales
        String nombre = Paths.get(originalName).getFileName().toString();
    
        // permitir solo letras, números, punto, guion y guion bajo
        if (!nombre.matches("[a-zA-Z0-9._-]+")) {
            throw new ServletException("Nombre de archivo inválido");
        }
    
        return nombre;
    }

    Qué hace

    • Evita nombres vacíos
    • Elimina rutas (C:\algo\malicioso\file.jpg)
    • Bloquea caracteres peligrosos
    • Reduce riesgo de path traversal

    2. Validar tipo MIME

    Evitar que suban archivos ejecutables disfrazados de imagen.

    Método

    private void validarTipoMime(Part part) throws ServletException {
    
        String mime = part.getContentType();
    
        if (mime == null || !mime.startsWith("image/")) {
            throw new ServletException("Solo se permiten archivos de imagen");
        }
    }

    Qué hace

    • Comprueba que el archivo es image/*
    • Bloquea .exe, .jsp, .php, etc.

    3. Validar extensión

    Permitir solo formatos conocidos.

    Método

    private void validarExtension(String fileName) throws ServletException {
    
        String ext = "";
    
        int punto = fileName.lastIndexOf('.');
        if (punto >= 0) {
            ext = fileName.substring(punto + 1).toLowerCase();
        }
    
        switch (ext) {
            case "jpg":
            case "jpeg":
            case "png":
            case "gif":
            case "webp":
                return;
            default:
                throw new ServletException("Extensión no permitida");
        }
    }

    Qué hace

    • Extrae la extensión
    • Permite solo imágenes conocidas
    • Evita subir scripts renombrados

    4. Validar tamaño del archivo

    Evitar archivos demasiado grandes.

    Método

    private void validarTamano(Part part, long maxBytes) throws ServletException {
    
        if (part.getSize() == 0) {
            throw new ServletException("El archivo está vacío");
        }
    
        if (part.getSize() > maxBytes) {
            throw new ServletException("El archivo supera el tamaño permitido");
        }
    }

    Uso típico

    validarTamano(part, 2*1024*1024); // 2 MB

    5. Validar que realmente sea imagen

    Evitar archivos falsos con extensión .jpg pero que no son imagen.

    Método

    private void validarContenidoImagen(InputStream input) throws ServletException, IOException {
    
        BufferedImage img = ImageIO.read(input);
    
        if (img == null) {
            throw new ServletException("El archivo no es una imagen válida");
        }
    }

    Qué hace

    • Intenta decodificar la imagen
    • Si falla → no es imagen real

    6. Evitar archivos duplicados

    No sobrescribir archivos existentes.

    Método

    private File evitarDuplicado(File carpeta, String fileName) {
    
        File file = new File(carpeta, fileName);
    
        if (!file.exists()) {
            return file;
        }
    
        String nombre = fileName;
        String base = nombre;
        String ext = "";
    
        int punto = nombre.lastIndexOf('.');
        if (punto >= 0) {
            base = nombre.substring(0, punto);
            ext = nombre.substring(punto);
        }
    
        int contador = 1;
        File nuevo;
    
        do {
            nuevo = new File(carpeta, base + "_" + contador + ext);
            contador++;
        } while (nuevo.exists());
    
        return nuevo;
    }

    Qué hace

    Si ya existe:

    dragon.jpg

    Genera:

    dragon_1.jpg
    dragon_2.jpg
    dragon_3.jpg

    7. Generar nombre seguro (UUID)

    Esto penalizaria el SEO si se trata de archivos que los buscadores pudiesen indexar

    Evitar colisiones y nombres maliciosos.

    Método

    private String generarNombreSeguro(String original) {
    
        String ext = "";
    
        int punto = original.lastIndexOf('.');
        if (punto >= 0) {
            ext = original.substring(punto);
        }
    
        return java.util.UUID.randomUUID().toString() + ext;
    }

    Qué hace

    Convierte:

    dragon.png

    en algo como:

    f47ac10b-58cc-4372-a567-0e02b2c3d479.png

    8. Crear carpeta si no existe

    private void asegurarCarpeta(File carpeta) throws ServletException {
    
        if (!carpeta.exists()) {
            if (!carpeta.mkdirs()) {
                throw new ServletException("No se pudo crear la carpeta de subida");
            }
        }
    }

    9. Ejemplo integrando todas las validaciones

    Este sería el flujo dentro de subirFoto():

    Part part = request.getPart("foto");
    
    validarTipoMime(part);
    validarTamano(part, 2 * 1024 * 1024);
    
    String nombreOriginal = validarNombreArchivo(part.getSubmittedFileName());
    validarExtension(nombreOriginal);
    
    InputStream input = part.getInputStream();
    validarContenidoImagen(input);
    
    // resetear stream tras validación
    input = part.getInputStream();
    
    asegurarCarpeta(uploads);
    
    String nombreSeguro = generarNombreSeguro(nombreOriginal);
    
    File destino = evitarDuplicado(uploads, nombreSeguro);
    
    Files.copy(input, destino.toPath());
    
    return destino.getName();


    Subir un archivo parece trivial, pero en realidad es una intersección entre E/S, seguridad, validación, arquitectura y persistencia. Cuando el alumno domina esto, deja de “subir fotos” y empieza a construir backend serio.

  • 3.1 – EL DOM (DOCUMENT OBJECT MODEL)

    3.1 – EL DOM (DOCUMENT OBJECT MODEL)

    1. ¿Qué es el DOM?

    El DOM (Document Object Model) es la representación en forma de árbol de objetos que el navegador crea a partir del HTML.

    El navegador no “lee HTML y ya”.
    Lo convierte en objetos manipulables con JavaScript.

    Ejemplo simple:

    HTML:

    <body>
      <h1>Hola</h1>
      <p>Texto</p>
    </body>

    El navegador lo convierte mentalmente en:

    document
     └── body
         ├── h1
         │   └── "Hola"
         └── p
             └── "Texto"

    Cada etiqueta es un nodo (node).
    Cada nodo es un objeto JavaScript.

    Eso significa que puedes:

    • Leerlo
    • Modificarlo
    • Crear nuevos nodos
    • Borrarlos
    • Moverlos

    El DOM es, literalmente, la memoria viva del HTML.


    2. El objeto document

    El punto de entrada al DOM es:

    document

    Es el objeto raíz del árbol. Desde aquí puedes acceder a todo el HTML.

    Ejemplo:

    console.log(document.title);
    console.log(document.body);

    3. Seleccionar elementos del DOM

    Antes de modificar algo, hay que localizar el nodo.

    3.1 getElementById (el clásico)

    HTML:

    document.querySelector("#titulo");      // por id
    document.querySelector(".clase");       // por clase
    document.querySelector("h1");           // por etiqueta
    document.querySelector("div p");        // descendiente

    JS:

    const titulo = document.getElementById("titulo");
    console.log(titulo);

    Devuelve un solo elemento.


    3.2 querySelector (el más versátil)

    Permite usar selectores CSS.

    document.querySelector("#titulo");      // por id
    document.querySelector(".clase");       // por clase
    document.querySelector("h1");           // por etiqueta
    document.querySelector("div p");        // descendiente

    Devuelve el primer elemento que coincide.


    3.3 querySelectorAll (varios elementos)

    const items = document.querySelectorAll("li");
    console.log(items);

    Devuelve una NodeList (colección).

    Se recorre así:

    items.forEach(item => {
      console.log(item.textContent);
    });

    4. Leer y modificar contenido

    4.1 textContent (forma segura)

    const titulo = document.getElementById("titulo");
    titulo.textContent = "Nuevo título";

    Nunca ejecuta HTML. Solo texto.


    4.2 innerHTML (usar con cuidado)

    titulo.innerHTML = "<b>Hola</b>";

    Inserta HTML real.
    Peligroso si el contenido viene del usuario (XSS).

    Regla:
    Si solo es texto → textContent.


    5. Manipular atributos

    Leer atributo

    const img = document.querySelector("img");
    console.log(img.getAttribute("src"));

    Modificar atributo

    img.setAttribute("src", "nueva.jpg");

    6. Manipular clases CSS

    Añadir clase

    elemento.classList.add("activo");

    Quitar clase

    elemento.classList.remove("activo");

    Alternar

    elemento.classList.toggle("activo");

    7. Crear elementos (crear nodos)

    Aquí empieza la verdadera manipulación del DOM.

    Crear un elemento

    const p = document.createElement("p");
    p.textContent = "Soy nuevo en el DOM";

    Todavía no está en la página.


    8. Insertar elementos en el DOM

    append (al final)

    document.body.append(p);

    prepend (al principio)

    document.body.prepend(p);

    append en otro nodo

    const contenedor = document.getElementById("contenedor");
    contenedor.append(p);

    9. Eliminar elementos

    p.remove();

    10. Ejercicio guiado — Pintar lista desde un array

    HTML:

    <ulid="lista"></ul>

    JS:

    const datos = ["Manzana", "Pera", "Plátano", "Kiwi"];
    
    const lista = document.getElementById("lista");
    
    datos.forEach(fruta => {
      const li = document.createElement("li");
      li.textContent = fruta;
      lista.append(li);
    });

    Resultado: el DOM se construye dinámicamente.


    11. Cómo piensa un programador al usar el DOM

    El flujo mental correcto es:

    1. Selecciono el nodo contenedor
    2. Creo elementos nuevos
    3. Les pongo contenido
    4. Los inserto en el DOM

    Es exactamente el mismo pensamiento que usarías manipulando objetos en Java.


    12. Errores comunes

    ❌ Usar innerHTML para todo

    Malo para seguridad y mantenimiento.

    ❌ Crear elementos sin insertarlos

    Si no haces append/prepend → no aparecen.

    ❌ Duplicar IDs

    Un ID debe ser único.

    ❌ Mezclar lógica y HTML sin orden

    Primero datos, luego render.


    13. Mini-reto

    Crea una tabla HTML y, desde JavaScript, genera 5 filas con:

    • Nombre
    • Edad
    • Ciudad

    Todo debe construirse con createElement.

    Nada de escribir filas en el HTML.


    14. Lo que debes haber aprendido

    Al terminar este bloque debes poder:

    • Entender qué es el DOM
    • Seleccionar elementos
    • Modificar texto
    • Manipular atributos y clases
    • Crear elementos dinámicamente
    • Insertar y eliminar nodos
    • Construir HTML desde datos

    Si esto está claro, en el siguiente bloque el cerebro da el salto natural:

    JSON → JavaScript → DOM

    Y ahí empieza el verdadero front conectado a tu backend JEE.

    El DOM no es difícil. Solo es un árbol.
    Y los árboles, curiosamente, se doman rama por rama. 🌿

    puedes hacerme una tabla con los metodos o atributos que puedo sacar de cada elemento.

    El DOM es como una criatura mitológica hecha de objetos. Cada elemento HTML hereda poderes de varias “clases ancestrales”: Node → Element → HTMLElement. Eso significa que casi todos los elementos comparten un mismo arsenal de propiedades y métodos. No memorizas etiquetas… entiendes la especie.

    Aquí tienes la tabla de referencia esencial que usarás constantemente.


    Métodos y atributos principales de un elemento DOM

    TipoNombreQué haceEjemplo
    📦 PropiedadidObtiene o cambia el id del elementoel.id = "nuevo"
    📦 PropiedadclassNameClases CSS como stringel.className = "rojo grande"
    📦 PropiedadclassListManejo avanzado de clasesel.classList.add("activo")
    📦 PropiedadtextContentTexto interno (seguro)el.textContent = "Hola"
    📦 PropiedadinnerHTMLHTML internoel.innerHTML = "<b>Hola</b>"
    📦 PropiedadchildrenElementos hijosel.children.length
    📦 PropiedadparentElementNodo padreel.parentElement
    📦 PropiedadstyleEstilos inlineel.style.color = "red"
    📦 PropiedadvalueValor (inputs, textarea)input.value
    📦 PropiedadcheckedEstado checkbox/radiocheck.checked
    📦 PropiedadsrcRuta de imagen/videoimg.src
    📦 PropiedadhrefEnlacesa.href
    📦 PropiedaddisabledHabilitar/deshabilitarbtn.disabled = true
    📦 PropiedaddatasetAcceso a data-*el.dataset.id

    Métodos de atributos

    MétodoQué haceEjemplo
    getAttribute(name)Lee atributoel.getAttribute("src")
    setAttribute(name, value)Cambia atributoel.setAttribute("alt","img")
    removeAttribute(name)Elimina atributoel.removeAttribute("disabled")
    hasAttribute(name)Comprueba atributoel.hasAttribute("id")

    Métodos de clases CSS

    MétodoQué haceEjemplo
    classList.add()Añade claseel.classList.add("rojo")
    classList.remove()Quita claseel.classList.remove("rojo")
    classList.toggle()Alterna claseel.classList.toggle("activo")
    classList.contains()Comprueba claseel.classList.contains("rojo")

    Métodos de creación y manipulación

    MétodoQué haceEjemplo
    createElement(tag)Crea nododocument.createElement("div")
    append()Inserta al finalpadre.append(hijo)
    prepend()Inserta al iniciopadre.prepend(hijo)
    remove()Elimina nodoel.remove()
    cloneNode(true)Clona nodoel.cloneNode(true)

    Métodos de búsqueda dentro de un elemento

    MétodoQué haceEjemplo
    querySelector()Primer descendienteel.querySelector("p")
    querySelectorAll()Todos los descendientesel.querySelectorAll("li")
    getElementsByClassName()Por claseel.getElementsByClassName("x")
    getElementsByTagName()Por etiquetael.getElementsByTagName("div")

    Eventos (interacción)

    MétodoQué haceEjemplo
    addEventListener()Escucha eventobtn.addEventListener("click", fn)
    removeEventListener()Quita eventobtn.removeEventListener("click", fn)

    Eventos comunes:

    • click
    • input
    • change
    • submit
    • keydown
    • mouseover

    PropiedadQué hace
    parentElementPadre
    childrenHijos elementos
    firstElementChildPrimer hijo
    lastElementChildÚltimo hijo
    nextElementSiblingHermano siguiente
    previousElementSiblingHermano anterior

  • 3.2 – EVENTOS EN JAVASCRIPT

    3.2 – EVENTOS EN JAVASCRIPT

    1. Qué es un evento

    Un evento es cualquier interacción que ocurre en la página:

    • El usuario hace clic
    • Escribe en un input
    • Envía un formulario
    • Mueve el ratón
    • Pulsa una tecla
    • La página termina de cargar

    Cuando ocurre algo → el navegador lo detecta → JavaScript puede reaccionar.

    El DOM no es solo un árbol… es un sistema reactivo.


    2. Qué es un Event Listener

    Un Event Listener es una función que espera a que ocurra un evento.

    Cuando el evento ocurre → se ejecuta la función.

    La forma moderna y correcta de escuchar eventos es:

    elemento.addEventListener("evento", funcion);

    3. Sintaxis de addEventListener

    boton.addEventListener("click", () => {
      console.log("Se hizo clic");
    });

    Parámetros:

    • Tipo de evento → "click"
    • Función a ejecutar → callback

    4. Ejemplo básico

    HTML:

    <buttonid="btn">Haz clic</button>

    JavaScript:

    const btn = document.getElementById("btn");
    
    btn.addEventListener("click", () => {
      alert("Botón pulsado");
    });

    5. El objeto event

    Cada vez que ocurre un evento, JavaScript genera un objeto llamado event que contiene información.

    btn.addEventListener("click", (event) => {
      console.log(event);
    });

    Propiedades útiles:

    PropiedadQué indica
    event.targetElemento que disparó el evento
    event.typeTipo de evento
    event.clientXPosición X del ratón
    event.clientYPosición Y del ratón
    event.keyTecla pulsada
    event.preventDefault()Evita comportamiento por defecto
    event.stopPropagation()Detiene propagación

    6. Eventos más comunes

    Click

    element.addEventListener("click", fn);

    Input (cuando el usuario escribe)

    input.addEventListener("input", fn);

    Change (cuando cambia valor)

    select.addEventListener("change", fn);

    Submit (formularios)

    form.addEventListener("submit", fn);

    Keydown (teclado)

    document.addEventListener("keydown", fn);

    Mouseover

    element.addEventListener("mouseover", fn);

    7. Evitar comportamiento por defecto

    Ejemplo: evitar que un formulario recargue la página.

    form.addEventListener("submit", (e) => {
      e.preventDefault();
      console.log("Formulario capturado");
    });

    8. Propagación de eventos (Bubbling)

    Cuando ocurre un evento, no solo afecta al elemento… sube por el árbol DOM.

    Ejemplo:

    <div id="padre">
      <button id="hijo">Click</button>
    </div>
    padre.addEventListener("click", () => console.log("Padre"));
    hijo.addEventListener("click", () => console.log("Hijo"));

    Resultado al hacer click:

    Hijo
    Padre

    Primero el elemento → luego sus padres.


    9. Detener propagación

    hijo.addEventListener("click", (e) => {
      e.stopPropagation();
    });

    10. Delegación de eventos (muy importante)

    En lugar de añadir eventos a cada elemento, se añade uno solo al contenedor.

    tabla.addEventListener("click", (e) => {
      if (e.target.classList.contains("btn-ver")) {
        const id = e.target.dataset.id;
        console.log("Ver registro", id);
      }
    });

    Ventajas:

    • Funciona con elementos creados dinámicamente
    • Mejor rendimiento
    • Menos código

    Esto es fundamental cuando el DOM se genera con JavaScript.


    11. Eliminar eventos

    function clickHandler() {
      console.log("Click");
    }
    
    btn.addEventListener("click", clickHandler);
    btn.removeEventListener("click", clickHandler);

    La función debe ser la misma referencia.


    12. Eventos en elementos creados dinámicamente

    Cuando creas un elemento con JavaScript, debes añadir el listener manualmente.

    const boton = document.createElement("button");
    boton.textContent = "Eliminar";
    
    boton.addEventListener("click", () => {
      console.log("Elemento eliminado");
    });

    Tabla de eventos en JavaScript

    🖱️ Eventos del ratón

    EventoCuándo ocurreUso típico
    clickClic normalBotones, enlaces
    dblclickDoble clicAcciones especiales
    mousedownPulsar botón ratónDrag, control
    mouseupSoltar botónFinalizar acción
    mousemoveMover ratónTracking, dibujo
    mouseoverEntrar en elementoHover
    mouseoutSalir de elementoQuitar hover
    mouseenterEntrar (sin bubbling)UI precisa
    mouseleaveSalir (sin bubbling)UI precisa
    contextmenuClic derechoMenú contextual

    ⌨️ Eventos del teclado

    EventoCuándo ocurreUso típico
    keydownTecla presionadaDetectar tecla
    keyupTecla liberadaConfirmar entrada
    keypressTecla mantenida (deprecated)Evitar usar

    Propiedad útil: event.key


    📝 Eventos de formularios / inputs

    EventoCuándo ocurreUso típico
    inputCambia el valor en tiempo realValidación viva
    changeValor confirmadoSelect, checkbox
    submitEnvío de formularioCapturar datos
    resetReset formularioRestaurar
    focusElemento recibe focoUX
    blurElemento pierde focoValidación
    invalidValidación HTML fallaFormularios

    📄 Eventos del documento / ventana

    EventoCuándo ocurreUso típico
    DOMContentLoadedDOM listoInicializar app
    loadPágina completamente cargadaRecursos
    resizeCambia tamaño ventanaResponsive
    scrollScroll de páginaLazy load
    beforeunloadAntes de salirConfirmación
    unloadPágina se cierraLimpieza

    🎯 Eventos de interacción avanzada

    EventoCuándo ocurreUso típico
    dragstartInicio dragDrag & Drop
    dragoverArrastrando encimaDrop zones
    dropSoltar elementoUpload
    touchstartTocar pantallaMóvil
    touchmoveDeslizarGestos
    touchendFin toqueMobile UI
    pointerdownInput universalMultiplataforma

    📡 Eventos de red y estado

    EventoCuándo ocurreUso típico
    onlineVuelve conexiónReintentar
    offlineSin conexiónAvisar
    errorError recursoDebug
    abortCancelaciónFetch

    🎬 Eventos multimedia

    EventoCuándo ocurreUso típico
    playReproducirVideo/audio
    pausePausarControl
    endedFinaliza mediaLoop
    timeupdateTiempo cambiaBarra progreso
    volumechangeVolumen cambiaUI

    🧠 Propiedades comunes del objeto event

    PropiedadQué indica
    event.targetElemento que disparó el evento
    event.currentTargetElemento con listener
    event.typeTipo de evento
    event.keyTecla
    event.clientX/YPosición ratón
    event.preventDefault()Evita comportamiento
    event.stopPropagation()Detiene bubbling

    Ejemplo guiado: cargar JSON por fetch al inicio y pintarlo en el DOM

    1. Al abrir la página, se ejecuta código al evento DOMContentLoaded
    2. Se llama a un endpoint con fetch()
    3. Se convierte la respuesta a JSON
    4. Se renderiza una tabla en el DOM
    5. Se manejan estados: cargando / error / sin datos

    1) HTML (estructura base)

    Crea un archivo index.html:

    <!doctype html>
    <html lang="es">
    <head>
      <meta charset="utf-8" />
      <meta name="viewport" content="width=device-width,initial-scale=1" />
      <title>JSON → DOM</title>
      <style>
        body { font-family: system-ui, Arial; padding: 20px; }
        .status { padding: 10px; border-radius: 8px; margin: 10px 0; }
        .loading { background: #fff3cd; }
        .error { background: #f8d7da; }
        .ok { background: #d1e7dd; }
        table { border-collapse: collapse; width: 100%; margin-top: 10px; }
        th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
        th { background: #f3f3f3; }
      </style>
    </head>
    
    <body>
      <h1>Listado de coches</h1>
    
      <div id="status" class="status loading">Cargando...</div>
    
      <table>
        <thead>
          <tr>
            <th>ID</th>
            <th>Marca</th>
            <th>Modelo</th>
            <th>Precio</th>
          </tr>
        </thead>
        <tbody id="rows"></tbody>
      </table>
    
      <script src="app.js"></script>
    </body>
    </html>

    Qué estamos preparando aquí

    • Un div#status para mostrar mensajes (cargando/error/vacío).
    • Un tbody#rows donde vamos a insertar filas dinámicamente.
    • Cargamos app.js al final.

    2) JavaScript (cargar y pintar)

    Crea un archivo app.js:

    // 1) Este evento se dispara cuando el DOM ya está construido.
    //    (Importante: aún puede que imágenes o recursos no hayan cargado, pero el HTML sí.)
    document.addEventListener("DOMContentLoaded", () => {
      loadCars();
    });
    
    // 2) Función principal: obtiene datos y renderiza
    async function loadCars() {
      try {
        setStatus("Cargando...", "loading");
    
        // Cambia esta URL por tu endpoint real (por ejemplo en JEE: /miApp/api/coches)
        const url = "coches.json"; // Para pruebas: archivo local con JSON
        const response = await fetch(url);
    
        // 3) Comprobamos si la respuesta HTTP es correcta
        if (!response.ok) {
          throw new Error(`HTTP ${response.status} - ${response.statusText}`);
        }
    
        // 4) Convertimos el body a JSON (array de objetos)
        const cars = await response.json();
    
        // 5) Si no hay datos, mostramos estado vacío
        if (!Array.isArray(cars) || cars.length === 0) {
          clearRows();
          setStatus("No hay datos disponibles.", "ok");
          return;
        }
    
        // 6) Pintamos los datos
        renderCars(cars);
        setStatus(`Cargados ${cars.length} registros.`, "ok");
    
      } catch (error) {
        clearRows();
        setStatus(`Error cargando datos: ${error.message}`, "error");
      }
    }
    
    // --- Renderizado ---
    
    function renderCars(cars) {
      const tbody = document.getElementById("rows");
      tbody.innerHTML = ""; // limpiamos antes de pintar
    
      // Creamos un fragmento para insertar todo de golpe (más eficiente)
      const fragment = document.createDocumentFragment();
    
      cars.forEach(car => {
        fragment.append(createRow(car));
      });
    
      tbody.append(fragment);
    }
    
    function createRow(car) {
      const tr = document.createElement("tr");
    
      const tdId = document.createElement("td");
      tdId.textContent = car.id;
    
      const tdMarca = document.createElement("td");
      tdMarca.textContent = car.marca;
    
      const tdModelo = document.createElement("td");
      tdModelo.textContent = car.modelo;
    
      const tdPrecio = document.createElement("td");
      tdPrecio.textContent = car.precio;
    
      tr.append(tdId, tdMarca, tdModelo, tdPrecio);
      return tr;
    }
    
    function clearRows() {
      document.getElementById("rows").innerHTML = "";
    }
    
    // --- Estado UI (mensajes) ---
    
    function setStatus(message, type) {
      const status = document.getElementById("status");
      status.textContent = message;
    
      // Reseteamos clases y aplicamos la nueva
      status.classList.remove("loading", "error", "ok");
      status.classList.add(type);
    }

    Qué has aprendido aquí

    • DOMContentLoaded asegura que el HTML existe antes de seleccionar nodos.
    • fetch() hace la petición.
    • response.ok comprueba si el HTTP fue bien.
    • response.json() parsea la respuesta.
    • renderCars() separa “pintar” de “cargar datos”.
    • setStatus() maneja estados.

    3) JSON de prueba (para que funcione sin backend)

    Crea coches.json en la misma carpeta:

    [
      { "id": 1, "marca": "Toyota", "modelo": "Corolla", "precio": 20000 },
      { "id": 2, "marca": "Ford", "modelo": "Focus", "precio": 18000 },
      { "id": 3, "marca": "Seat", "modelo": "Ibiza", "precio": 15500 }
    ]

    4) Nota importante (muy típica en clase)

    Si abres index.html haciendo doble clic (ruta file://), en algunos navegadores el fetch("coches.json") puede fallar por permisos.

    Soluciones sencillas para clase:

    • Servirlo desde un servidor local (ideal).
    • Si ya estás con JEE, sirve la página desde tu propio proyecto y el endpoint desde el servlet.

    5) Conectarlo a JEE

    Cuando tengas tu endpoint en JEE, normalmente será algo así:

    • URL: /miApp/api/coches
    • Devuelve JSON

    Solo cambias:

    consturl="/miApp/api/coches";

    Y listo.