# ─────────────────────────────────────────────
# MODIFICAR LISTAS: ÍNDICES Y SLICES
# ─────────────────────────────────────────────
# Importación de un módulo propio (archivo iterar_cadenas.py)
# Por ahora no te preocupes por esto, lo veremos más adelante
from iterar_cadenas import posicion
lista = [1, 2, 3, 4, 5, 6, 7]
# Cambiar UN elemento por su índice (posición)
# El índice 3 corresponde al 4º elemento (se empieza a contar desde 0)
lista[3] = 11
print(lista) # → [1, 2, 3, 11, 5, 6, 7]
# SLICE (rebanada): modificar VARIOS elementos a la vez
# lista[4:7] selecciona desde el índice 4 hasta el 6 (el 7 no se incluye)
# Los reemplazamos por tres nuevos valores
lista[4:7] = [91, 92, 93]
print(lista) # → [1, 2, 3, 11, 91, 92, 93]
# Un slice puede reemplazarse por MENOS elementos de los que había
# Aquí sustituimos 2 elementos (índices 0 y 1) por solo 1
lista[0:2] = [1]
print(lista) # → [1, 3, 11, 91, 92, 93] ← la lista se encoge
# También puede reemplazarse por MÁS elementos de los que había
# Aquí sustituimos 2 elementos por 9 → la lista crece
lista[0:2] = [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(lista) # → [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 91, 92, 93]
# ─────────────────────────────────────────────
# EXTEND: añadir elementos de otro iterable
# ─────────────────────────────────────────────
listilla = [1, 2, 3]
otralista = [7, 77, 777]
tupla = (8, 88, 888)
cadena = "abc"
# extend() añade cada elemento del iterable uno a uno al final de la lista
listilla.extend(otralista) # Añade los elementos de una lista
print(listilla) # → [1, 2, 3, 7, 77, 777]
listilla.extend(tupla) # También funciona con tuplas
print(listilla) # → [1, 2, 3, 7, 77, 777, 8, 88, 888]
listilla.extend(cadena) # Con una cadena añade CADA LETRA por separado
print(listilla) # → [1, 2, 3, 7, 77, 777, 8, 88, 888, 'a', 'b', 'c']
# ─────────────────────────────────────────────
# EXTEND vs APPEND: diferencia importante
# ─────────────────────────────────────────────
lista = [1, 2, 3]
lista.extend([4, 5]) # extend: desglosa la lista y añade cada elemento
print(lista) # → [1, 2, 3, 4, 5]
lista = [1, 2, 3]
lista.append([4, 5]) # append: añade el objeto ENTERO como un único elemento
print(lista) # → [1, 2, 3, [4, 5]] ← ¡el último elemento es una lista!
# ─────────────────────────────────────────────
# LISTAS ANIDADAS (listas dentro de listas)
# ─────────────────────────────────────────────
# Cada alumno es una lista con su nombre y una tupla de notas
alumnos = [
["Ana", (1, 2, 3)],
["Pep", (4, 5, 6)],
["Iu", (7, 8)]
]
# Para acceder: alumnos[0] → ["Ana", (1,2,3)]
# alumnos[0][1] → (1, 2, 3)
# alumnos[0][1][0] → 1
# ─────────────────────────────────────────────
# MÉTODOS PARA ELIMINAR ELEMENTOS
# ─────────────────────────────────────────────
frutas = ["pera", "manzana", "kiwi", "pera"]
# 'in' comprueba si un elemento existe en la lista
if "pera" in frutas:
frutas.remove("pera") # remove() elimina la PRIMERA ocurrencia del valor
print(frutas) # → ['manzana', 'kiwi', 'pera'] ← queda una "pera"
# pop() elimina y DEVUELVE el último elemento (sin argumentos)
ultima_fruta = frutas.pop()
print(ultima_fruta) # → 'pera'
print(frutas) # → ['manzana', 'kiwi']
# pop(índice) elimina y devuelve el elemento en esa posición
primera_fruta = frutas.pop(0)
print(primera_fruta) # → 'manzana'
print(frutas) # → ['kiwi']
# ─────────────────────────────────────────────
# BUSCAR ELEMENTOS: index()
# ─────────────────────────────────────────────
lista = [2, 3, 4, 5, 4, 3, 2, 7, 8, 2]
# index(valor) devuelve la posición de la PRIMERA aparición del valor
posicion = lista.index(2)
print(posicion) # → 0
# index(valor, inicio) busca a partir de una posición concreta
# Así podemos encontrar la 2ª, 3ª... aparición del mismo valor
posicion = lista.index(2, posicion + 1) # Busca desde la posición 1 en adelante
print(posicion) # → 6
posicion = lista.index(2, posicion + 1) # Busca desde la posición 7 en adelante
print(posicion) # → 9
# ─────────────────────────────────────────────
# ORDENAR LISTAS: sort()
# ─────────────────────────────────────────────
lista = [2, 3, 4, 5, 4, 3, 2, 7, 8, 2]
lista.sort() # Orden ascendente (de menor a mayor)
print(lista) # → [2, 2, 2, 3, 3, 4, 4, 5, 7, 8]
lista.sort(reverse=True) # Orden descendente (de mayor a menor)
print(lista) # → [8, 7, 5, 4, 4, 3, 3, 2, 2, 2]
# sort(key=función): ordena según el valor que devuelve la función
# para cada elemento. Aquí los pares se quedan igual y los impares
# se "pesan" el doble, por lo que los pares van primero.
def mifuncion(num):
if num % 2 == 0:
return num # Par: se usa su valor real
else:
return num * 2 # Impar: se usa el doble (ocupa más "peso" al ordenar)
lista.sort(key=mifuncion)
print(lista) # → [2, 2, 2, 3, 4, 4, 3, 5, 7, 8] (pares primero)
# Ordenar cadenas por su longitud usando len como función clave
lista = ["Ana", "Iu", "Eva", "Pep", "Rosa", "Juan", "Roc"]
lista.sort(key=len) # len devuelve el número de caracteres de cada nombre
print(lista) # → ['Iu', 'Ana', 'Eva', 'Pep', 'Roc', 'Rosa', 'Juan']
# ─────────────────────────────────────────────
# COPIA DE LISTAS: referencia vs copia real
# ─────────────────────────────────────────────
notas = [6, 7, 3, 5]
# ⚠️ ASIGNACIÓN DIRECTA: NO crea una copia, ambas variables apuntan
# al MISMO objeto en memoria. Cambiar una cambia la otra.
copia = notas
copia[1] = 9
print(copia) # → [6, 9, 3, 5]
print(notas) # → [6, 9, 3, 5] ← ¡notas también cambió! Cuidado con esto.
notas = [6, 7, 3, 5]
# ✅ COPIA REAL con .copy(): crea un objeto nuevo e independiente
# Modificar 'copia' NO afecta a 'notas'
copia = notas.copy()
copia[1] = 9
print(copia) # → [6, 9, 3, 5]
print(notas) # → [6, 7, 3, 5] ← notas no ha cambiado ✓
# ─────────────────────────────────────────────
# SLICES (REBANADAS): acceder a partes de una cadena o lista
# ─────────────────────────────────────────────
# Sintaxis general: coleccion[inicio:fin:paso]
# · inicio: índice donde empieza la rebanada (se incluye)
# · fin: índice donde termina (NO se incluye)
# · paso: de cuánto en cuánto avanza (por defecto 1)
cadena = "Mi mamá me mima"
#índices: 0123456789...
# [inicio:fin] → caracteres desde el índice 2 hasta el 5 (el 6 no se incluye)
print(cadena[2:6]) # → ' mam'
# [:fin] → desde el principio hasta el índice 1 (el 2 no se incluye)
print(cadena[:2]) # → 'Mi'
# [inicio:] → desde el índice 5 hasta el final
print(cadena[5:]) # → 'má me mima'
# Índices NEGATIVOS: cuentan desde el final hacia atrás
# -1 es el último carácter, -2 el penúltimo, etc.
# [-3:] → los últimos 3 caracteres
print(cadena[-3:]) # → 'ima'
# ─────────────────────────────────────────────
# LOS MISMOS SLICES FUNCIONAN IGUAL CON LISTAS
# ─────────────────────────────────────────────
lista = ["Ana", "Iu", "Eva", "Pep", "Rosa", "Juan", "Roc"]
#índices: 0 1 2 3 4 5 6
print(lista[2:6]) # → ['Eva', 'Pep', 'Rosa', 'Juan'] (del índice 2 al 5)
print(lista[:2]) # → ['Ana', 'Iu'] (los 2 primeros)
print(lista[5:]) # → ['Juan', 'Roc'] (desde el índice 5 hasta el final)
print(lista[-3:]) # → ['Juan', 'Roc', ... ] (los 3 últimos)
# ─────────────────────────────────────────────
# EL TERCER PARÁMETRO: el PASO
# ─────────────────────────────────────────────
# [::] → sin inicio, sin fin, paso por defecto (1) → copia entera
print(lista[::]) # → ['Ana', 'Iu', 'Eva', 'Pep', 'Rosa', 'Juan', 'Roc']
# [::2] → coge un elemento sí, uno no (paso 2)
# índices 0, 2, 4, 6 → "Ana", "Eva", "Rosa", "Roc"
print(lista[::2]) # → ['Ana', 'Eva', 'Rosa', 'Roc']
# [::-1] → paso NEGATIVO: recorre la lista al revés
# sin inicio ni fin → la lista completa invertida
print(lista[::-1]) # → ['Roc', 'Juan', 'Rosa', 'Pep', 'Eva', 'Iu', 'Ana']
# [::-2] → al revés de dos en dos
# índices 6, 4, 2, 0 → "Roc", "Rosa", "Eva", "Ana"
print(lista[::-2]) # → ['Roc', 'Rosa', 'Eva', 'Ana']
# [6:2:-1] → hacia atrás desde el índice 6 hasta el 3 (el 2 no se incluye)
# índices 6, 5, 4, 3 → "Roc", "Juan", "Rosa", "Pep"
print(lista[6:2:-1]) # → ['Roc', 'Juan', 'Rosa', 'Pep']