En esta página

Listas y tuplas

14 min lectura TextoCap. 2 — Funciones y datos

Listas en Python

Las listas son la estructura de datos más versátil de Python. Son colecciones ordenadas y mutables que pueden contener elementos de cualquier tipo, incluso mezclados:

# Crear listas
vacía = []
numeros = [1, 2, 3, 4, 5]
textos = ["Python", "JavaScript", "Rust"]
mixta = [1, "hola", True, 3.14, None]
anidada = [[1, 2], [3, 4], [5, 6]]

# list() — crear desde otros iterables
desde_rango = list(range(1, 6))      # [1, 2, 3, 4, 5]
desde_string = list("Python")        # ['P', 'y', 't', 'h', 'o', 'n']
desde_tupla = list((1, 2, 3))        # [1, 2, 3]

# Longitud
print(len(numeros))    # 5
print(len(vacía))      # 0

Indexación

Python usa indexación basada en cero. Los índices negativos cuentan desde el final:

frutas = ["manzana", "pera", "uva", "naranja", "kiwi"]

# Índices positivos
print(frutas[0])   # "manzana"
print(frutas[1])   # "pera"
print(frutas[4])   # "kiwi"

# Índices negativos
print(frutas[-1])  # "kiwi"    — último
print(frutas[-2])  # "naranja" — penúltimo
print(frutas[-5])  # "manzana" — primero desde el final

# Acceso a listas anidadas
matriz = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
print(matriz[1][2])   # 6 — fila 1, columna 2
print(matriz[0][0])   # 1 — primera fila, primera columna

Slicing (rebanado)

El slicing permite extraer sublistas con la sintaxis lista[inicio:fin:paso]. El índice fin es exclusivo:

letras = ["a", "b", "c", "d", "e", "f", "g"]

# Casos básicos
print(letras[1:4])    # ['b', 'c', 'd']  — índices 1, 2, 3
print(letras[:3])     # ['a', 'b', 'c']  — desde el inicio hasta 3 (exclusivo)
print(letras[4:])     # ['e', 'f', 'g']  — desde índice 4 hasta el final
print(letras[:])      # ['a', 'b', 'c', 'd', 'e', 'f', 'g'] — copia completa
print(letras[-3:])    # ['e', 'f', 'g']  — últimos 3 elementos

# Con paso
print(letras[::2])    # ['a', 'c', 'e', 'g'] — cada segundo elemento
print(letras[1::2])   # ['b', 'd', 'f']       — pares (índices impares)
print(letras[::-1])   # ['g', 'f', 'e', 'd', 'c', 'b', 'a'] — invertida
print(letras[6:1:-2]) # ['g', 'e', 'c']        — hacia atrás de 2 en 2

# Asignación mediante slicing
numeros = [0, 1, 2, 3, 4, 5]
numeros[1:4] = [10, 20]  # Reemplaza 3 elementos con 2
print(numeros)           # [0, 10, 20, 4, 5]

numeros[2:2] = [100, 200]  # Inserción sin reemplazar
print(numeros)             # [0, 10, 100, 200, 20, 4, 5]

Métodos de lista

Las listas tienen métodos que permiten modificarlas. Es importante entender si el método modifica la lista in-place o devuelve un nuevo objeto:

tareas = ["estudiar", "programar", "descansar"]

# append() — agrega un elemento al final (in-place, devuelve None)
tareas.append("hacer ejercicio")
print(tareas)  # ['estudiar', 'programar', 'descansar', 'hacer ejercicio']

# extend() — agrega múltiples elementos al final (in-place)
tareas.extend(["leer", "meditar"])
print(tareas)

# insert() — inserta en una posición específica (in-place)
tareas.insert(1, "tomar café")  # Inserta en índice 1
print(tareas[0:3])  # ['estudiar', 'tomar café', 'programar']

# remove() — elimina la primera ocurrencia de un valor (in-place)
tareas.remove("tomar café")

# pop() — elimina y devuelve un elemento (in-place)
ultima = tareas.pop()       # Elimina el último
print(ultima)               # 'meditar'
segunda = tareas.pop(1)     # Elimina por índice
print(segunda)              # 'programar'

# clear() — vacía la lista (in-place)
temp = [1, 2, 3]
temp.clear()
print(temp)  # []

# index() — devuelve el índice de la primera ocurrencia
nums = [10, 20, 30, 20, 40]
print(nums.index(20))       # 1 — primer 20
print(nums.index(20, 2))    # 3 — busca desde índice 2

# count() — cuenta ocurrencias
print(nums.count(20))  # 2

# sort() — ordena in-place (no devuelve la lista)
nombres = ["Carlos", "Ana", "Bruno", "Diana"]
nombres.sort()
print(nombres)  # ['Ana', 'Bruno', 'Carlos', 'Diana']

nombres.sort(key=str.lower, reverse=True)  # Con criterio personalizado
print(nombres)  # ['Diana', 'Carlos', 'Bruno', 'Ana']

# sorted() — devuelve nueva lista ordenada (no modifica la original)
original = [3, 1, 4, 1, 5, 9, 2, 6]
ordenada = sorted(original)
print(original)  # [3, 1, 4, 1, 5, 9, 2, 6] — sin cambios
print(ordenada)  # [1, 1, 2, 3, 4, 5, 6, 9]

# reverse() — invierte in-place
nombres.reverse()
print(nombres)  # ['Ana', 'Bruno', 'Carlos', 'Diana'] (ya estaba ordenada al revés)

# copy() — copia superficial
original = [1, 2, 3]
copia = original.copy()   # Equivalente a original[:]
copia.append(4)
print(original)  # [1, 2, 3] — no afectado
print(copia)     # [1, 2, 3, 4]

Tuplas: inmutabilidad por diseño

Las tuplas son colecciones ordenadas e inmutables. Se crean con paréntesis o simplemente con comas:

# Crear tuplas
vacía = ()
un_elemento = (42,)    # La coma es necesaria para un solo elemento
coordenadas = (3.5, 7.2)
rgb = (255, 128, 0)
punto_3d = (1, 2, 3)

# Sin paréntesis (empaquetado automático)
dimensiones = 1920, 1080
print(type(dimensiones))  # <class 'tuple'>

# tuple() — convertir desde iterables
desde_lista = tuple([1, 2, 3])    # (1, 2, 3)
desde_string = tuple("Python")   # ('P', 'y', 't', 'h', 'o', 'n')

# Inmutabilidad
coords = (10, 20)
# coords[0] = 5  # TypeError: 'tuple' object does not support item assignment

# Pero si contiene mutables, esos sí se pueden modificar
tupla_con_lista = ([1, 2], [3, 4])
tupla_con_lista[0].append(100)  # ¡Esto sí funciona!
print(tupla_con_lista)  # ([1, 2, 100], [3, 4])

Desempaquetado de tuplas y listas

El desempaquetado es una de las características más elegantes de Python:

# Desempaquetado básico
punto = (3, 7)
x, y = punto
print(f"x={x}, y={y}")  # x=3, y=7

# Desempaquetado con asterisco
primero, *resto = [1, 2, 3, 4, 5]
print(primero)  # 1
print(resto)    # [2, 3, 4, 5]

*inicio, ultimo = [1, 2, 3, 4, 5]
print(inicio)   # [1, 2, 3, 4]
print(ultimo)   # 5

primero, *medio, ultimo = [1, 2, 3, 4, 5]
print(primero)  # 1
print(medio)    # [2, 3, 4]
print(ultimo)   # 5

# Intercambio de variables (sin variable temporal)
a, b = 10, 20
a, b = b, a
print(a, b)  # 20 10

# Desempaquetado anidado
matriz = [(1, 'a'), (2, 'b'), (3, 'c')]
for num, letra in matriz:
    print(f"{num}{letra}")

# Ignorar valores con _
datos = ("Ana", 28, "Madrid", "[email protected]")
nombre, _, ciudad, _ = datos
print(nombre, ciudad)  # Ana Madrid

enumerate() — índice y valor simultáneamente

lenguajes = ["Python", "JavaScript", "Rust", "Go", "TypeScript"]

# Forma incorrecta (anti-patrón)
for i in range(len(lenguajes)):
    print(f"{i}: {lenguajes[i]}")

# Forma Pythónica — con enumerate()
for i, lenguaje in enumerate(lenguajes):
    print(f"{i}: {lenguaje}")

# Con inicio personalizado
for pos, lenguaje in enumerate(lenguajes, start=1):
    print(f"{pos}. {lenguaje}")
# 1. Python
# 2. JavaScript
# 3. Rust
# 4. Go
# 5. TypeScript

# enumerate devuelve pares (índice, valor) como tuplas
pares = list(enumerate(lenguajes))
print(pares)  # [(0, 'Python'), (1, 'JavaScript'), ...]

zip() — iterar múltiples secuencias en paralelo

nombres = ["Ana", "Bruno", "Carmen", "Diego"]
edades = [25, 30, 28, 35]
ciudades = ["Madrid", "Barcelona", "Sevilla", "Valencia"]

# zip() crea pares de elementos
for nombre, edad, ciudad in zip(nombres, edades, ciudades):
    print(f"{nombre} ({edad}) — {ciudad}")

# zip se detiene en la secuencia más corta
a = [1, 2, 3, 4, 5]
b = ["a", "b", "c"]
print(list(zip(a, b)))  # [(1, 'a'), (2, 'b'), (3, 'c')]

# zip_longest — usa el más largo (rellena con None o valor personalizado)
from itertools import zip_longest
print(list(zip_longest(a, b, fillvalue="?")))
# [(1, 'a'), (2, 'b'), (3, 'c'), (4, '?'), (5, '?')]

# Descomprimir con zip(*...)
pares = [(1, "uno"), (2, "dos"), (3, "tres")]
numeros, palabras = zip(*pares)
print(numeros)   # (1, 2, 3)
print(palabras)  # ('uno', 'dos', 'tres')

Operaciones comunes con listas

# Concatenación
lista_a = [1, 2, 3]
lista_b = [4, 5, 6]
combinada = lista_a + lista_b
print(combinada)  # [1, 2, 3, 4, 5, 6]

# Repetición
patron = [0, 1]
repetida = patron * 4
print(repetida)  # [0, 1, 0, 1, 0, 1, 0, 1]

# Pertenencia
print(3 in lista_a)      # True
print(10 not in lista_a) # True

# Funciones integradas útiles con listas
numeros = [3, 1, 4, 1, 5, 9, 2, 6, 5]
print(min(numeros))    # 1
print(max(numeros))    # 9
print(sum(numeros))    # 36
print(len(numeros))    # 9

# any() y all()
booleanos = [True, True, False, True]
print(any(booleanos))  # True — al menos uno es True
print(all(booleanos))  # False — no todos son True

numeros_positivos = [1, 3, 5, 7]
print(all(n > 0 for n in numeros_positivos))  # True
print(any(n > 6 for n in numeros_positivos))  # True

Listas anidadas (matrices)

# Crear una matriz 3x3
matriz = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

# Acceder a elementos
print(matriz[1][2])  # 6

# Iterar filas y columnas
for fila in matriz:
    for elemento in fila:
        print(elemento, end=" ")
    print()

# Transponer una matriz
def transponer(mat: list[list[int]]) -> list[list[int]]:
    return [[mat[j][i] for j in range(len(mat))] for i in range(len(mat[0]))]

transpuesta = transponer(matriz)
for fila in transpuesta:
    print(fila)
# [1, 4, 7]
# [2, 5, 8]
# [3, 6, 9]

Resumen

Las listas son la estructura de datos secuencial más poderosa de Python: mutables, indexables con valores negativos y con slicing avanzado. Las tuplas ofrecen inmutabilidad para datos que no deben cambiar. enumerate() y zip() son tus aliados para bucles elegantes. En la próxima lección dominaremos los diccionarios y conjuntos, las estructuras de datos clave para búsquedas eficientes.

Lista[:] hace una copia superficial (shallow copy)
lista[:] o list(lista) copian la lista pero no los objetos que contiene. Si la lista contiene otras listas, modificar los objetos internos afectará ambas listas. Para una copia profunda usa copy.deepcopy().
Las tuplas son más eficientes que las listas
Las tuplas ocupan menos memoria y son más rápidas de crear e iterar que las listas. Úsalas cuando los datos no deben cambiar, como coordenadas, configuraciones o registros de base de datos.
Usa enumerate() en lugar de range(len(lista))
El patrón for i in range(len(lista)): lista[i] es considerado anti-Pythónico. Usa for i, elemento in enumerate(lista): para obtener índice y valor de forma elegante.