Sesión 5 - Control de Flujo Avanzado y Listas en Python ======================================================= Introducción ----------- El control de flujo y las estructuras de datos son componentes fundamentales en la programación. En esta sesión, exploraremos el control de flujo avanzado en Python y las listas como una de las estructuras de datos más versátiles del lenguaje. También introduciremos brevemente las tuplas como una estructura de datos relacionada. Estos conceptos son esenciales para crear programas más potentes y elegantes. Control de Flujo con break, continue y pass ------------------------------------------- Cuando trabajamos con bucles en Python, a veces necesitamos controlar cómo se ejecutan de manera más precisa. Para esto, Python ofrece tres palabras clave especiales: **break** La instrucción ``break`` termina inmediatamente el bucle actual y sale de él, como si se hubiera llegado al final. Es muy útil cuando encontramos lo que estábamos buscando o cuando queremos salir de un bucle basándonos en alguna condición: .. code-block:: python # Ejemplo de break for i in range(10): # Crea una secuencia: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 if i == 5: break # Sale del bucle inmediatamente cuando i es 5 print(i) # Imprime: 0, 1, 2, 3, 4 print("Fuera del bucle") # Este código se ejecuta después de salir del bucle otro ejemplo: .. code-block:: python # Buscar un elemento en una lista numeros = [4, 7, 2, 9, 1, 5] objetivo = 9 indice = 0 encontrado = False # Variable para recordar si encontramos el número for valor in numeros: if valor == objetivo: print(f"¡Encontrado! El número {objetivo} está en la posición {indice}") encontrado = True break # Salimos del bucle, ya no necesitamos seguir buscando indice += 1 # Si no encontramos el número, mostramos un mensaje diferente if not encontrado: print(f"El número {objetivo} no está en la lista") Un uso muy común de ``break`` es con ``while True``, que normalmente crearía un bucle infinito, pero con ``break`` podemos salir cuando queramos: .. code-block:: python # Ejemplo con while - un bucle que pide datos hasta que digamos "no" respuesta = "" while True: # Bucle infinito que se ejecutará hasta que usemos break respuesta = input("¿Desea continuar? (s/n): ") if respuesta.lower() == 'n': # Si responde "n" o "N" break # Salimos del bucle print("Continuamos en el bucle...") print("Ha salido del bucle") **continue** La instrucción ``continue`` salta a la siguiente iteración del bucle sin ejecutar el resto del código dentro del bucle para esa iteración. Es como decir "ignora el resto de esta vuelta y pasa a la siguiente": .. code-block:: python # Ejemplo de continue - imprimir solo números impares for i in range(10): # Secuencia: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 if i % 2 == 0: # Si el número es par (divisible por 2) continue # Salta esta iteración, no ejecuta el código siguiente print(i) # Solo se ejecuta si i NO es par # Imprime: 1, 3, 5, 7, 9 El ``continue`` es muy útil cuando queremos filtrar o ignorar ciertos elementos: .. code-block:: python # Procesar solo textos no vacíos textos = ["Hola", "", "Python", "Programación", ""] for texto in textos: if texto == "": # Si es texto vacío print("Texto vacío encontrado, ignorando...") continue # Salta a la siguiente iteración # Este código solo se ejecuta para textos no vacíos print(f"Texto: {texto}, Longitud: {len(texto)}") # Saltar iteraciones problemáticas numeros = [1, 2, 0, 4, 0, 5] for num in numeros: if num == 0: # Evitamos dividir por cero print("No se puede dividir por cero, saltando...") continue # Salta a la siguiente iteración resultado = 10 / num # Esta línea no se ejecuta si num es 0 print(f"10 / {num} = {resultado}") **pass** La instrucción ``pass`` no hace absolutamente nada. Es como un marcador de posición que dice "aquí va código, pero aún no lo he escrito". A diferencia de ``continue``, no altera el flujo del bucle: .. code-block:: python # Ejemplo de pass for i in range(5): # Secuencia: 0, 1, 2, 3, 4 if i == 2: pass # No hace nada, simplemente continúa con el bucle print(i) # Imprime: 0, 1, 2, 3, 4 (todos los números, pass no afecta al flujo) El ``pass`` es especialmente útil durante el desarrollo cuando aún no tenemos claro qué código escribir: .. code-block:: python # En estructuras condicionales que requieren acción nula edad = 25 if edad < 18: # Aquí iría código para menores, pero aún no lo implementamos pass # El pass evita errores de sintaxis al dejar la estructura vacía elif edad > 65: # Código para jubilados, pendiente de implementar pass else: print("Adulto en edad laboral") **Resumen de break, continue y pass:** - ``break``: Sale completamente del bucle. - ``continue``: Salta a la siguiente iteración del bucle. - ``pass``: No hace nada, solo sirve como marcador de posición. Listas en Python --------------- Las listas son una de las estructuras de datos más importantes y utilizadas en Python. Permiten almacenar múltiples valores en una sola variable. Fundamentos de Listas ^^^^^^^^^^^^^^^^^^^^^ Las listas en Python son como "cajas" que pueden contener varios elementos en un orden específico. Sus principales características son: - **Ordenadas**: Los elementos mantienen el orden en que los colocamos. - **Modificables**: Podemos cambiar, añadir o quitar elementos después de crear la lista. - **Indexadas**: Cada elemento tiene una posición numérica (índice) que comienza en 0. - **Pueden contener cualquier tipo de datos**: Números, textos, booleanos o incluso otras listas. .. code-block:: python # Creación de listas lista_vacia = [] # Una lista sin elementos numeros = [1, 2, 3, 4, 5] # Lista de números mixta = [1, "Hola", 3.14, True] # Lista con diferentes tipos de datos # Acceso a elementos por índice primer_elemento = numeros[0] # El primer elemento es 1 (¡los índices empiezan en 0!) segundo_elemento = numeros[1] # El segundo elemento es 2 ultimo_elemento = numeros[-1] # El último elemento es 5 (índice negativo) penultimo = numeros[-2] # El penúltimo elemento es 4 print(f"Primer elemento: {primer_elemento}") print(f"Último elemento: {ultimo_elemento}") # Modificación de elementos numeros[2] = 30 # Cambia el tercer elemento (índice 2) a 30 print(f"Lista modificada: {numeros}") # [1, 2, 30, 4, 5] # Longitud de una lista (cuántos elementos tiene) longitud = len(numeros) # 5 print(f"La lista tiene {longitud} elementos") # Verificar si un elemento existe en la lista con 'in' existe = 4 in numeros # True, porque 4 está en la lista no_existe = 6 in numeros # False, porque 6 no está en la lista print(f"¿El número 4 está en la lista? {existe}") print(f"¿El número 6 está en la lista? {no_existe}") **Acceso a elementos de una lista con índices negativos:** Los índices negativos cuentan desde el final de la lista: .. code-block:: python colores = ["rojo", "verde", "azul", "amarillo"] print(colores[-1]) # "amarillo" (último elemento) print(colores[-2]) # "azul" (penúltimo elemento) print(colores[-3]) # "verde" print(colores[-4]) # "rojo" (primer elemento) Slicing (Rebanado) Básico ^^^^^^^^^^^^^^^^^^^^^^^^^ El slicing es una técnica muy poderosa que permite extraer partes de una lista. Se usa la sintaxis ``lista[inicio:fin]`` o ``lista[inicio:fin:paso]``: - **inicio**: Posición donde comienza el slice (se incluye este elemento). - **fin**: Posición donde termina el slice (este elemento NO se incluye). - **paso**: Opcional, indica el "salto" entre elementos seleccionados. Si omitimos algún valor, Python usará por defecto el inicio (0), el fin (longitud de la lista), o un paso de 1. .. code-block:: python numeros = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] # Slicing básico primeros_tres = numeros[0:3] # [0, 1, 2] (elementos de índice 0, 1 y 2) # Podemos omitir el 0 inicial: primeros_tres = numeros[:3] # [0, 1, 2] (lo mismo que arriba) # Desde un punto hasta el final desde_cuarto = numeros[3:] # [3, 4, 5, 6, 7, 8, 9] (desde índice 3 hasta el final) # Hasta cierto punto desde el inicio hasta_quinto = numeros[:5] # [0, 1, 2, 3, 4] (desde el inicio hasta índice 4) print(f"Primeros tres elementos: {primeros_tres}") print(f"Desde el cuarto elemento: {desde_cuarto}") print(f"Hasta el quinto elemento: {hasta_quinto}") # Slicing con índices negativos ultimos_tres = numeros[-3:] # [7, 8, 9] (los últimos 3 elementos) todos_menos_dos = numeros[:-2] # [0, 1, 2, 3, 4, 5, 6, 7] (todos excepto los últimos 2) print(f"Últimos tres elementos: {ultimos_tres}") print(f"Todos menos los últimos dos: {todos_menos_dos}") # Copiando una lista completa con slicing copia_lista = numeros[:] # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] (copia toda la lista) # Slicing con paso (salto entre elementos) cada_segundo = numeros[::2] # [0, 2, 4, 6, 8] (toma elementos saltando de 2 en 2) cada_tercero = numeros[::3] # [0, 3, 6, 9] (toma elementos saltando de 3 en 3) print(f"Cada segundo elemento: {cada_segundo}") print(f"Cada tercer elemento: {cada_tercero}") # Slicing inverso (paso negativo) reverso = numeros[::-1] # [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] (toda la lista al revés) reverso_salto = numeros[::-2] # [9, 7, 5, 3, 1] (elementos al revés, saltando de 2 en 2) print(f"Lista al revés: {reverso}") print(f"Lista al revés saltando de 2 en 2: {reverso_salto}") # Combinación de inicio, fin y paso rango_con_paso = numeros[1:8:2] # [1, 3, 5, 7] (desde índice 1 hasta 7, saltando de 2 en 2) print(f"Desde índice 1 hasta 7, saltando de 2 en 2: {rango_con_paso}") Diferencia entre Asignación y Copia por Slicing ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Es muy importante entender la diferencia entre copiar una referencia a una lista y crear una copia independiente: - **lista1 = lista2**: No crea una nueva lista. Ambas variables apuntan a la misma lista en memoria. Si modificas la lista usando cualquiera de las variables, verás los cambios con ambas. - **lista1 = lista2[:]**: Crea una copia nueva e independiente de todos los elementos. Si modificas una lista, la otra no cambiará. .. code-block:: python # Ejemplo de referencia vs copia original = [1, 2, 3] # Referencia (ambas variables apuntan al mismo objeto en memoria) referencia = original referencia.append(4) # Añade 4 a la lista a través de la variable 'referencia' print(f"Original después de modificar referencia: {original}") # [1, 2, 3, 4] print(f"Referencia: {referencia}") # [1, 2, 3, 4] # ¡original también cambió porque ambas variables apuntan a la misma lista! # Copia por slicing (crea una nueva lista en memoria) original = [1, 2, 3] # Reiniciamos la lista original copia = original[:] # Crea una copia de todos los elementos copia.append(4) # Modifica solo la copia print(f"Original después de modificar copia: {original}") # [1, 2, 3] - original no cambia print(f"Copia: {copia}") # [1, 2, 3, 4] - solo la copia tiene el nuevo elemento Esta diferencia es crucial. Muchos errores en programación ocurren cuando modificamos una lista pensando que estamos trabajando con una copia independiente, cuando en realidad estamos cambiando la lista original. Métodos de Listas ^^^^^^^^^^^^^^^^^ Python proporciona muchos métodos incorporados para manipular listas. Estos son algunos de los más importantes: **Métodos para Agregar Elementos:** .. code-block:: python numeros = [1, 2, 3] # append() - Agregar un elemento al final numeros.append(4) print(f"Después de append(4): {numeros}") # [1, 2, 3, 4] # insert() - Insertar en posición específica (índice, valor) numeros.insert(1, 1.5) # Inserta 1.5 en la posición 1 (segundo elemento) print(f"Después de insert(1, 1.5): {numeros}") # [1, 1.5, 2, 3, 4] # extend() - Añadir varios elementos (otra lista) al final numeros.extend([5, 6]) # Añade todos los elementos de [5, 6] al final print(f"Después de extend([5, 6]): {numeros}") # [1, 1.5, 2, 3, 4, 5, 6] Diferencia entre `extend()` y el Operador `+` """"""""""""""""""""""""""""""""""""""""""""" Ambos combinan listas, pero funcionan de manera diferente: - **lista1.extend(lista2)**: Modifica la lista original añadiendo todos los elementos de la segunda lista. - **lista1 + lista2**: Crea una nueva lista con los elementos de ambas, sin modificar las originales. .. code-block:: python # Usando extend() (modifica la lista original) numeros1 = [1, 2, 3] numeros2 = [4, 5, 6] # Guardamos una referencia para ver después print(f"ID de numeros1 antes: {id(numeros1)}") numeros1.extend(numeros2) # Modifica numeros1 print(f"numeros1 después de extend: {numeros1}") # [1, 2, 3, 4, 5, 6] print(f"numeros2 sigue igual: {numeros2}") # [4, 5, 6] print(f"ID de numeros1 después: {id(numeros1)}") # Mismo ID # Usando el operador + (crea una nueva lista) numeros1 = [1, 2, 3] # Reiniciamos numeros1 numeros2 = [4, 5, 6] print(f"ID de numeros1 antes: {id(numeros1)}") numeros3 = numeros1 + numeros2 # Crea una nueva lista sin cambiar las originales print(f"numeros1 sigue igual: {numeros1}") # [1, 2, 3] print(f"numeros2 sigue igual: {numeros2}") # [4, 5, 6] print(f"numeros3 (nueva lista): {numeros3}") # [1, 2, 3, 4, 5, 6] print(f"ID de numeros3: {id(numeros3)}") # ID diferente El método `extend()` es generalmente más eficiente para listas grandes porque modifica la lista existente en lugar de crear una nueva. **Métodos para Eliminar Elementos:** .. code-block:: python numeros = [1, 2, 3, 4, 3, 5] # remove() - Elimina la primera ocurrencia de un valor numeros.remove(3) # Elimina el primer 3 que encuentra print(f"Después de remove(3): {numeros}") # [1, 2, 4, 3, 5] # pop() con índice - Elimina el elemento en la posición indicada y lo devuelve valor = numeros.pop(2) # Elimina el elemento en la posición 2 (tercer elemento) print(f"Elemento eliminado con pop(2): {valor}") # 4 print(f"Lista después de pop(2): {numeros}") # [1, 2, 3, 5] # pop() sin índice - Elimina y devuelve el último elemento ultimo = numeros.pop() # Elimina el último elemento print(f"Último elemento: {ultimo}") # 5 print(f"Lista después de pop(): {numeros}") # [1, 2, 3] # clear() - Elimina todos los elementos numeros.clear() print(f"Lista después de clear(): {numeros}") # [] **Otros métodos útiles para eliminar elementos:** .. code-block:: python # Eliminar por índice con del (no devuelve el valor) numeros = [10, 20, 30, 40, 50] del numeros[2] # Elimina el elemento en índice 2 (tercer elemento) print(f"Después de del numeros[2]: {numeros}") # [10, 20, 40, 50] # Eliminar un rango de elementos con del y slicing numeros = [10, 20, 30, 40, 50] del numeros[1:3] # Elimina los elementos con índices 1 y 2 print(f"Después de del numeros[1:3]: {numeros}") # [10, 40, 50] **Métodos para Buscar y Contar:** .. code-block:: python numeros = [1, 2, 3, 4, 3, 5] # count() - Contar ocurrencias de un valor conteo = numeros.count(3) # Cuenta cuántos 3 hay en la lista print(f"El número 3 aparece {conteo} veces") # 2 # index() - Encontrar la posición de un valor (primera ocurrencia) indice = numeros.index(3) # Busca la primera ocurrencia de 3 print(f"La primera ocurrencia de 3 está en la posición {indice}") # 2 # Si intentamos buscar un elemento que no existe con index(), obtendremos un error try: indice = numeros.index(6) # Esto causará un error except ValueError: print("Error: El número 6 no está en la lista") **Métodos para Ordenar y Revertir:** .. code-block:: python numeros = [3, 1, 4, 2, 5] print(f"Lista original: {numeros}") # sort() - Ordenar la lista original (modifica la lista) numeros.sort() # Ordena de menor a mayor print(f"Lista ordenada: {numeros}") # [1, 2, 3, 4, 5] # sort(reverse=True) - Ordenar en orden descendente numeros.sort(reverse=True) # Ordena de mayor a menor print(f"Lista ordenada descendente: {numeros}") # [5, 4, 3, 2, 1] # reverse() - Invertir el orden actual (sin ordenar) numeros = [3, 1, 4, 2, 5] # Reiniciamos la lista print(f"Lista original: {numeros}") numeros.reverse() # Invierte la lista print(f"Lista invertida: {numeros}") # [5, 2, 4, 1, 3] **Otros métodos útiles:** .. code-block:: python # sorted() - Crear una nueva lista ordenada sin modificar la original numeros = [3, 1, 4, 2, 5] ordenados = sorted(numeros) # No modifica numeros, crea una nueva lista print(f"Lista original: {numeros}") # [3, 1, 4, 2, 5] print(f"Nueva lista ordenada: {ordenados}") # [1, 2, 3, 4, 5] # max() y min() - Encontrar el valor máximo y mínimo maximo = max(numeros) # 5 minimo = min(numeros) # 1 print(f"Valor máximo: {maximo}, Valor mínimo: {minimo}") # sum() - Sumar todos los elementos (solo funciona si todos son números) suma = sum(numeros) # 15 print(f"Suma de los elementos: {suma}") Listas y Ciclos For ^^^^^^^^^^^^^^^^^^ Las listas y los ciclos ``for`` son una combinación muy potente en Python. Hay varias formas de recorrer (iterar) una lista: Iteración Básica ^^^^^^^^^^^^^^^ .. code-block:: python frutas = ["manzana", "banana", "cereza", "durazno"] # Forma 1: Iterando directamente sobre los elementos print("Iterando directamente sobre los elementos:") for fruta in frutas: print(f"- {fruta}") # Forma 2: Iterando sobre los índices print("\nIterando sobre los índices:") for i in range(len(frutas)): print(f"En la posición {i} está: {frutas[i]}") # Forma 3: Usando enumerate() para obtener tanto el índice como el valor print("\nUsando enumerate():") for indice, fruta in enumerate(frutas): print(f"Índice {indice}: {fruta}") Modificación Durante la Iteración ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Es muy importante tener cuidado al modificar una lista mientras la recorres. Hacerlo puede causar comportamientos inesperados o errores: .. code-block:: python # ¡INCORRECTO! Modificar la lista mientras iteramos sobre ella puede causar problemas numeros = [1, 2, 3, 4, 5] print(f"Lista original: {numeros}") for numero in numeros: if numero % 2 == 0: # Si el número es par numeros.remove(numero) # Esto alterará los índices durante la iteración print(f"Lista después de eliminar (manera incorrecta): {numeros}") # Resultado puede ser [1, 3, 5] o [1, 3, 4] dependiendo de cómo se ejecute # CORRECTO: Crear una nueva lista filtrada numeros = [1, 2, 3, 4, 5] numeros_impares = [] for numero in numeros: if numero % 2 != 0: # Si el número es impar numeros_impares.append(numero) print(f"Nueva lista con solo impares: {numeros_impares}") # [1, 3, 5] # CORRECTO: Iterar sobre una copia si necesitamos modificar la original numeros = [1, 2, 3, 4, 5] for numero in numeros[:]: # Usamos slicing [:] para crear una copia if numero % 2 == 0: numeros.remove(numero) print(f"Lista después de eliminar (manera correcta): {numeros}") # [1, 3, 5] Comprensiones de Listas Básicas ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Las comprensiones de listas son una manera elegante y concisa de crear nuevas listas basadas en listas existentes u otros iterables. La sintaxis básica es: .. code-block:: python # Sintaxis básica [expresion for elemento in iterable] # Con condición [expresion for elemento in iterable if condicion] Es como escribir un bucle for, pero todo en una sola línea y dentro de corchetes. Ejemplos prácticos: .. code-block:: python # Ejemplo 1: Crear una lista de cuadrados # Forma tradicional con for cuadrados_tradicional = [] for x in range(1, 6): # Para números del 1 al 5 cuadrados_tradicional.append(x * x) print(f"Cuadrados (forma tradicional): {cuadrados_tradicional}") # Misma operación con comprensión de lista cuadrados = [x * x for x in range(1, 6)] print(f"Cuadrados (comprensión): {cuadrados}") # [1, 4, 9, 16, 25] # Ejemplo 2: Filtrar números pares numeros = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] # Forma tradicional con for pares_tradicional = [] for x in numeros: if x % 2 == 0: # Si x es par pares_tradicional.append(x) print(f"Números pares (forma tradicional): {pares_tradicional}") # Misma operación con comprensión de lista pares = [x for x in numeros if x % 2 == 0] print(f"Números pares (comprensión): {pares}") # [2, 4, 6, 8, 10] # Ejemplo 3: Convertir palabras a mayúsculas palabras = ["python", "es", "genial"] # Forma tradicional mayusculas_tradicional = [] for palabra in palabras: mayusculas_tradicional.append(palabra.upper()) print(f"Palabras en mayúsculas (forma tradicional): {mayusculas_tradicional}") # Con comprensión de lista mayusculas = [palabra.upper() for palabra in palabras] print(f"Palabras en mayúsculas (comprensión): {mayusculas}") # ["PYTHON", "ES", "GENIAL"] Las comprensiones de listas hacen que el código sea más limpio y a menudo más rápido, especialmente para operaciones simples en listas. Introducción a las Tuplas ------------------------ Las tuplas son muy similares a las listas, pero con una diferencia fundamental: son **inmutables**. Esto significa que una vez creadas, no puedes añadir, eliminar o cambiar sus elementos. Características de las tuplas: - **Ordenadas**: Mantienen el orden de los elementos. - **Inmutables**: No se pueden modificar después de crearse. - **Indexadas**: Los elementos se acceden por índice, igual que en las listas. - **Heterogéneas**: Pueden contener diferentes tipos de datos. .. code-block:: python # Creación de tuplas tupla_vacia = () # IMPORTANTE: Para tuplas de un solo elemento, necesitas una coma tupla_un_elemento = (1,) # Sin la coma sería solo el número 1, no una tupla coordenadas = (10, 20) # Tupla de dos elementos persona = ("Juan", 25, "Ingeniero") # Tupla con diferentes tipos de datos # También se pueden crear sin paréntesis otra_tupla = 1, 2, 3 # Equivalente a (1, 2, 3) print(f"Tupla de coordenadas: {coordenadas}") print(f"Tupla de persona: {persona}") print(f"Otra tupla: {otra_tupla}") # Acceso a elementos (igual que en listas) x = coordenadas[0] # 10 nombre = persona[0] # "Juan" print(f"Coordenada x: {x}") print(f"Nombre: {nombre}") # Intento de modificación (generará error) # coordenadas[0] = 15 # TypeError: 'tuple' object does not support item assignment # Desempaquetado: asignar elementos a variables individuales nombre, edad, profesion = persona print(f"Nombre: {nombre}") # "Juan" print(f"Edad: {edad}") # 25 print(f"Profesión: {profesion}") # "Ingeniero" # Slicing (igual que en listas) numeros = (0, 1, 2, 3, 4, 5) subset = numeros[2:5] # (2, 3, 4) print(f"Subconjunto: {subset}") **¿Cuándo usar tuplas en lugar de listas?** 1. **Para datos que no deben cambiar**: Como coordenadas geográficas, fechas, o información personal. 2. **Para garantizar que los datos no se modifiquen accidentalmente**: Las tuplas protegen contra cambios no deseados. 3. **Para datos heterogéneos que forman una "unidad"**: Como registros de una persona (nombre, edad, profesión). 4. **Para representar estructuras de datos fijas**: Como los colores RGB (rojo, verde, azul). .. code-block:: python # Ejemplos de uso de tuplas # Tuplas como registros fijos estudiantes = [ ("Ana", 20, "Matemáticas"), ("Carlos", 22, "Física"), ("Elena", 19, "Informática") ] # Iterando sobre tuplas (muy común) print("\nLista de estudiantes:") for nombre, edad, carrera in estudiantes: print(f"{nombre} tiene {edad} años y estudia {carrera}") # Coordenadas como valores inmutables puntos = [(0, 0), (5, 0), (5, 5), (0, 5)] # Lista de tuplas for x, y in puntos: print(f"Punto en ({x}, {y})") # Colores RGB (valores que forman una unidad lógica) colores = [ ("rojo", (255, 0, 0)), ("verde", (0, 255, 0)), ("azul", (0, 0, 255)) ] for nombre, (r, g, b) in colores: print(f"El color {nombre} tiene valores RGB: ({r}, {g}, {b})") **Comparación rápida: Listas vs Tuplas** +----------------+--------------------------+------------------------+ | Característica | Listas | Tuplas | +================+==========================+========================+ | Mutabilidad | Mutables (modificables) | Inmutables (fijas) | +----------------+--------------------------+------------------------+ | Sintaxis | [elemento1, elemento2] | (elemento1, elemento2) | +----------------+--------------------------+------------------------+ | Métodos | Muchos (append, remove, | Pocos (count, index) | | | sort, etc.) | | +----------------+--------------------------+------------------------+ | Uso típico | Colecciones de datos | Registros, coordenadas,| | | del mismo tipo que | valores fijos que no | | | pueden cambiar | deben cambiar | +----------------+--------------------------+------------------------+ Ejercicios Prácticos ------------------- Ejercicio 1: Control de Flujo con break ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Escribe un programa que pida números al usuario hasta que introduzca un número negativo. Luego, muestra la suma de todos los números positivos introducidos. .. toggle:: .. code-block:: python # Solución suma = 0 print("Introduce números positivos para sumar (introduce un número negativo para terminar)") while True: # Pedimos un número al usuario entrada = input("Introduce un número: ") # Convertimos la entrada a entero numero = int(entrada) # Verificamos si es negativo para salir del bucle if numero < 0: print("Has introducido un número negativo. Terminando...") break # Si llegamos aquí, el número es positivo o cero suma += numero print(f"Suma actual: {suma}") print(f"La suma total de los números introducidos es: {suma}") Este programa implementa una calculadora de suma continua que se detiene cuando el usuario introduce un número negativo: 1. Inicializamos una variable `suma` en 0 para guardar el acumulado. 2. Usamos un bucle `while True` para crear un ciclo infinito (que solo se interrumpirá con `break`). 3. En cada iteración: 4. - Solicitamos un número al usuario con `input()` - Lo convertimos a entero con `int()` - Si el número es negativo, mostramos un mensaje y usamos `break` para salir del bucle - Si no es negativo, lo sumamos al acumulado y mostramos la suma parcial 5. Al final, mostramos la suma total de todos los números positivos introducidos. El comando `break` es fundamental en este ejemplo pues permite terminar el bucle cuando se cumple la condición de salida. Ejercicio 2: Manipulación de Listas ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Escribe un programa que cree una lista con los números del 1 al 10, luego elimine los números pares, y finalmente imprima la lista resultante en orden inverso. .. toggle:: .. code-block:: python # Solución # 1. Crear lista del 1 al 10 numeros = [] for i in range(1, 11): numeros.append(i) print("Lista original:", numeros) # 2. Eliminar números pares # Opción 1: Usando un bucle while (cuidado con los índices) i = 0 while i < len(numeros): if numeros[i] % 2 == 0: # Si el número es par numeros.pop(i) # Eliminamos el elemento en posición i # No incrementamos i porque ahora hay un nuevo elemento en esta posición else: i += 1 # Solo avanzamos si no eliminamos el elemento print("Lista sin números pares:", numeros) # Opción 2 (alternativa más sencilla): Filtrar creando una nueva lista # numeros = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] # numeros = [n for n in numeros if n % 2 != 0] # print("Lista sin números pares:", numeros) # 3. Invertir la lista numeros.reverse() # Invertimos la lista actual print("Lista invertida:", numeros) # [9, 7, 5, 3, 1] # Otra forma de invertir usando slicing: # numeros = numeros[::-1] # print("Lista invertida con slicing:", numeros) Este programa realiza tres operaciones principales sobre listas: 1. **Creación de lista**: Utilizamos un bucle `for` con `range(1, 11)` para generar números del 1 al 10 y los añadimos a una lista vacía con `.append()`. 2. **Eliminación de pares**: Se implementan dos estrategias: - La solución principal usa un bucle `while` que recorre la lista por índices. Cuando encuentra un número par, lo elimina con `.pop(i)` sin avanzar el índice (porque al eliminar un elemento, el siguiente "cae" a la posición actual). Solo incrementamos el índice cuando no eliminamos elementos. - La alternativa comentada utiliza una comprensión de lista para crear una nueva lista filtrando los impares, lo que es más conciso pero crea una nueva lista en lugar de modificar la original. 3. **Inversión**: También se muestran dos técnicas: - El método `.reverse()` que invierte la lista in-place (sin crear una nueva). - El slicing `[::-1]` (comentado) que crea una nueva lista invertida. Este ejercicio ilustra la importancia de tener cuidado con los índices al eliminar elementos durante la iteración de una lista. Ejercicio 3: Comprensiones de Listas Básicas ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Utiliza una comprensión de lista para crear una lista con los cuadrados de los números impares del 1 al 20. .. toggle:: .. code-block:: python # Solución con comprensión de lista # 1. Generamos los números del 1 al 20 # 2. Filtramos quedándonos solo con los impares (x % 2 != 0) # 3. Calculamos sus cuadrados (x**2) impares_cuadrados = [x**2 for x in range(1, 21) if x % 2 != 0] print("Cuadrados de números impares del 1 al 20:") print(impares_cuadrados) # Resultado: [1, 9, 25, 49, 81, 121, 169, 225, 289, 361] # Para comparar, solución con bucle for tradicional impares_cuadrados_alt = [] for x in range(1, 21): if x % 2 != 0: # Si es impar impares_cuadrados_alt.append(x**2) print("Solución alternativa con bucle for tradicional:") print(impares_cuadrados_alt) # Resultado idéntico: [1, 9, 25, 49, 81, 121, 169, 225, 289, 361] Este programa muestra cómo usar comprensiones de lista para generar los cuadrados de números impares: 1. **Comprensión de lista**: La solución principal utiliza una sintaxis concisa que combina tres operaciones: - La iteración sobre un rango (`for x in range(1, 21)`) - El filtrado mediante una condición (`if x % 2 != 0`, que selecciona números impares) - La transformación mediante una expresión (`x**2`, que calcula el cuadrado) Todo esto se realiza en una sola línea que resulta en la lista deseada. 2. **Enfoque tradicional**: Para comparar, se muestra la versión equivalente usando un bucle `for` explícito: - Se crea una lista vacía - Se itera sobre el mismo rango - Se verifica la condición de imparidad con un `if` - Se añade el cuadrado a la lista con `.append()` La comprensión de lista ofrece una sintaxis más compacta y legible que suele ser más eficiente en términos de rendimiento. Esta es una técnica muy común en Python para operaciones de transformación y filtrado de datos. Ejercicio 4: Trabajando con Tuplas ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Crea una lista de tuplas donde cada tupla contenga un número del 1 al 5 y su cuadrado. Luego, recorre la lista e imprime cada par de valores. .. toggle:: .. code-block:: python # Solución # 1. Crear la lista de tuplas pares_numero_cuadrado = [] for i in range(1, 6): # Números del 1 al 5 # Creamos una tupla con el número y su cuadrado par = (i, i**2) # Añadimos la tupla a la lista pares_numero_cuadrado.append(par) print("Lista de tuplas creada:") print(pares_numero_cuadrado) # Resultado: [(1, 1), (2, 4), (3, 9), (4, 16), (5, 25)] # 2. Recorrer la lista e imprimir valores print("\nRecorriendo las tuplas:") for numero, cuadrado in pares_numero_cuadrado: print(f"El cuadrado de {numero} es {cuadrado}") # Solución alternativa con comprensión de lista print("\nCreando la misma lista con comprensión de lista:") pares_alternativa = [(i, i**2) for i in range(1, 6)] print(pares_alternativa) # Mismo resultado: [(1, 1), (2, 4), (3, 9), (4, 16), (5, 25)] Este programa muestra cómo trabajar con listas de tuplas en Python: 1. **Creación de la estructura de datos**: - Primero se utiliza un enfoque tradicional: un bucle `for` que crea tuplas (pares ordenados inmutables) con cada número y su cuadrado. - Cada tupla se añade a una lista usando `.append()`, resultando en una estructura de datos compuesta. 2. **Desempaquetado de tuplas**: - Al recorrer la lista, utilizamos el desempaquetado de tuplas con `for numero, cuadrado in pares_numero_cuadrado`. - Esta sintaxis asigna automáticamente el primer elemento de cada tupla a `numero` y el segundo a `cuadrado`. - Esta es una técnica muy común y elegante en Python para trabajar con secuencias de valores relacionados. 3. **Solución con comprensión**: - Se muestra una alternativa más concisa utilizando una comprensión de lista que crea directamente las tuplas. - Esta versión es más compacta pero produce exactamente el mismo resultado. Las tuplas son ideales para representar registros o grupos de datos relacionados cuando la cantidad de elementos es fija y conocida. Ejercicio 5: Uso práctico de break y continue ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Escribe un programa que pida al usuario introducir palabras y las guarde en una lista. El programa debe terminar cuando el usuario ingrese "fin". Además, si el usuario ingresa una palabra vacía o que ya existe en la lista, debe ignorarla y pedir otra. .. toggle:: .. code-block:: python # Solución palabras = [] # Lista para almacenar las palabras print("Introduce palabras (escribe 'fin' para terminar):") while True: # Pedimos una palabra al usuario palabra = input("Palabra: ").strip().lower() # Quitamos espacios y convertimos a minúsculas # Verificamos si el usuario quiere terminar if palabra == "fin": print("Programa terminado.") break # Verificamos si es palabra vacía if palabra == "": print("Palabra vacía, ignorando...") continue # Verificamos si la palabra ya existe en la lista if palabra in palabras: print(f"La palabra '{palabra}' ya está en la lista, ignorando...") continue # Si llegamos aquí, añadimos la palabra a la lista palabras.append(palabra) print(f"Palabra '{palabra}' añadida. Lista actual: {palabras}") # Mostramos la lista final print(f"\nLista final de palabras: {palabras}") print(f"Total de palabras únicas: {len(palabras)}") Este programa demuestra el uso de `break` y `continue` en un contexto práctico de recogida de datos: 1. **Estructura principal**: - Se inicia una lista vacía para almacenar palabras únicas. - Se usa un bucle `while True` para solicitar palabras indefinidamente. - El procesamiento termina solo cuando se recibe la palabra "fin". 2. **Preprocesamiento de entrada**: - La entrada del usuario se limpia con `.strip()` para eliminar espacios en blanco. - Se convierte a minúsculas con `.lower()` para hacer las comparaciones sin distinguir mayúsculas/minúsculas. 3. **Control de flujo avanzado**: - Se usa `break` para salir completamente del bucle cuando el usuario escribe "fin". - Se usa `continue` en dos casos: * Cuando se detecta una entrada vacía (después de quitar espacios). * Cuando la palabra ya existe en la lista (manteniendo solo palabras únicas). - El comando `continue` salta al inicio del bucle, evitando añadir la palabra a la lista. 4. **Verificación de existencia**: - El operador `in` verifica si un elemento está en una lista, lo que se usa para prevenir duplicados. Este ejemplo ilustra un patrón común en programas interactivos: la recolección de datos con validación y un mecanismo para terminar el proceso. Conclusión --------- En esta sesión, hemos aprendido conceptos fundamentales de control de flujo avanzado y estructuras de datos en Python: 1. **Control de flujo avanzado**: - ``break``: Sale completamente de un bucle. - ``continue``: Salta a la siguiente iteración del bucle. - ``pass``: No hace nada, solo sirve como marcador de posición. 2. **Listas**: - Son colecciones ordenadas y modificables de elementos. - Permiten acceso por índice, comenzando por 0. - Ofrecen muchos métodos como ``append()``, ``insert()``, ``remove()``, ``pop()``, etc. - Se pueden rebanar (slice) para extraer porciones con la sintaxis ``lista[inicio:fin:paso]``. 3. **Formas de recorrer listas**: - Directamente sobre elementos: ``for elemento in lista`` - Con índices: ``for i in range(len(lista))`` - Con índices y valores: ``for indice, valor in enumerate(lista)`` 4. **Comprensiones de listas**: - Ofrecen una forma concisa y elegante de crear listas. - Sintaxis: ``[expresion for elemento in iterable if condicion]`` 5. **Tuplas**: - Son similares a las listas pero inmutables (no se pueden modificar). - Son útiles para datos que no deben cambiar. - Permiten desempaquetado de elementos a variables individuales. Estos conceptos son esenciales para cualquier programador en Python, ya que te permiten: - Escribir código más eficiente y limpio - Controlar el flujo de ejecución con precisión - Manejar colecciones de datos de manera efectiva - Implementar algoritmos y solucionar problemas complejos A medida que sigas practicando, estos conceptos se volverán parte natural de tu forma de pensar cuando programes. Recuerda siempre que la mejor manera de aprender es escribiendo código y experimentando con ejemplos propios. Referencias ---------- * Documentación oficial de Python: https://docs.python.org/es/3/tutorial/datastructures.html * W3Schools - Python Lists: https://www.w3schools.com/python/python_lists.asp * Programiz - Python List: https://www.programiz.com/python-programming/list * Python for Beginners - Tuples: https://www.pythonforbeginners.com/basics/python-tuples * Real Python - Lists and Tuples: https://realpython.com/python-lists-tuples/ Tips Adicionales -------------- - **Practica regularmente**: La programación se aprende haciendo, no solo leyendo. - **Experimenta con ejemplos pequeños**: Modifica los ejemplos y observa qué sucede. - **No temas a los errores**: Son parte del proceso de aprendizaje y te ayudan a entender mejor cómo funciona Python. - **Escribe código legible**: Usa nombres descriptivos para variables y comenta tu código cuando sea necesario. - **Resuelve problemas gradualmente**: Divide los problemas grandes en pasos más pequeños y manejables.