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:
# 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:
# 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:
# 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»:
# 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:
# 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:
# 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:
# 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.
# 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:
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.
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á.
# 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:
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.
# 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:
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:
# 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:
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:
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:
# 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¶
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:
# ¡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:
# 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:
# 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.
# 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?
Para datos que no deben cambiar: Como coordenadas geográficas, fechas, o información personal.
Para garantizar que los datos no se modifiquen accidentalmente: Las tuplas protegen contra cambios no deseados.
Para datos heterogéneos que forman una «unidad»: Como registros de una persona (nombre, edad, profesión).
Para representar estructuras de datos fijas: Como los colores RGB (rojo, verde, azul).
# 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, sort, etc.) |
Pocos (count, index) |
Uso típico |
Colecciones de datos del mismo tipo que pueden cambiar |
Registros, coordenadas, valores fijos que no 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.
# 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:
Inicializamos una variable suma en 0 para guardar el acumulado.
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
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.
# 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:
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().
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.
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.
# 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:
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.
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.
# 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:
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.
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.
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.
# 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:
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».
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.
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.
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:
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.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 sintaxislista[inicio:fin:paso].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)Comprensiones de listas: - Ofrecen una forma concisa y elegante de crear listas. - Sintaxis:
[expresion for elemento in iterable if condicion]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.