Listas en Python: la estructura de datos más versátil
- Qué es una lista y cuándo usarla
- Crear listas y acceder por índice
- Slicing: extraer sublistas con elegancia
- Métodos esenciales: append, insert, remove, pop…
- Ordenar listas: sort() vs sorted()
- Copiar listas: la trampa de la referencia
- Listas anidadas y matrices
- Patrones idiomáticos con listas
- Programa completo: gestor de tareas
- Errores clásicos
- Preguntas frecuentes
lista.sort(): ordenar no destruye la colección, la revela. Y habría anotado en su Organon: usa append() para añadir al final, insert(0, x) para añadir al principio — y nunca modifiques una lista mientras la iteras.La lista es la estructura de datos más usada en Python. Es una secuencia ordenada y mutable que puede contener elementos de cualquier tipo, incluso mezclados y repetidos. A diferencia de los arrays en C o Java, la lista de Python no requiere declarar el tipo ni el tamaño de antemano: crece y encoge según necesites.
📋 Crear listas y acceder por índice
La sintaxis más directa para crear una lista es con corchetes [], separando los elementos con comas. Python índica las posiciones empezando desde 0 para el primer elemento, y admite índices negativos que cuentan desde el final.
# Crear listas
vacia = []
numeros = [1, 2, 3, 4, 5]
frutas = ["manzana", "pera", "naranja", "uva"]
mixta = [42, "hola", True, 3.14, None] # tipos mezclados
anidada = [[1, 2], [3, 4], [5, 6]] # lista de listas
# Otras formas de crear listas
desde_range = list(range(1, 6)) # [1, 2, 3, 4, 5]
desde_str = list("Python") # ['P','y','t','h','o','n']
repetida = [0] * 5 # [0, 0, 0, 0, 0]
repetida2 = ["ok"] * 3 # ['ok', 'ok', 'ok']
# Acceso por índice positivo (empieza en 0)
frutas[0] # "manzana" ← primer elemento
frutas[1] # "pera"
frutas[3] # "uva" ← último (índice = longitud - 1)
# Acceso por índice negativo (desde el final)
frutas[-1] # "uva" ← último
frutas[-2] # "naranja" ← penúltimo
frutas[-4] # "manzana" ← equivale a frutas[0]
# Longitud de la lista
len(frutas) # 4
# Comprobar si un elemento existe
"pera" in frutas # True
"kiwi" in frutas # False
"kiwi" not in frutas # True
# IndexError si el índice no existe
frutas[10] # IndexError: list index out of range
✂️ Slicing: extraer sublistas con elegancia
El slicing (rebanado) permite extraer partes de una lista con la sintaxis [inicio:fin:paso]. El índice fin es siempre excluido. Cualquiera de los tres valores puede omitirse, tomando sus valores por defecto: inicio = 0, fin = len(lista), paso = 1.
L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# Sintaxis básica: [inicio:fin] (fin excluido)
L[2:5] # [2, 3, 4] — índices 2, 3, 4
L[:3] # [0, 1, 2] — desde el principio hasta el 2
L[7:] # [7, 8, 9] — desde el 7 hasta el final
L[:] # [0,1,2,...,9] — copia completa (útil para copiar)
# Con índices negativos
L[-3:] # [7, 8, 9] — los últimos 3
L[:-3] # [0,1,2,3,4,5,6] — todo excepto los últimos 3
# Con paso
L[::2] # [0, 2, 4, 6, 8] — cada 2 elementos (índices pares)
L[1::2] # [1, 3, 5, 7, 9] — índices impares
L[::-1] # [9, 8, 7, ..., 0] — invertir la lista
L[1:8:3] # [1, 4, 7] — inicio=1, fin=8, paso=3
# Slicing en strings (misma sintaxis)
"Python"[::-1] # "nohtyP"
"abcdefg"[2:5] # "cde"
# Modificar una porción con slicing
L = [0, 1, 2, 3, 4, 5]
L[1:4] = [10, 20] # reemplaza índices 1,2,3 → [0, 10, 20, 4, 5]
L[2:2] = [99, 98] # inserta sin eliminar → [0, 10, 99, 98, 20, 4, 5]
L[1:3] = [] # elimina esa porción
# Copia superficial con slicing
original = [1, 2, 3]
copia = original[:] # nuevo objeto, mismos valores
copia.append(4)
print(original) # [1, 2, 3] ← no afectado
-1 es el último elemento, -2 el penúltimo. L[-3:] significa "los últimos tres", sea cual sea la longitud de la lista.
🔧 Métodos esenciales: append, insert, remove, pop…
Las listas en Python son objetos y tienen métodos propios para modificarlas. Todos los métodos que modifican la lista operan in-place (modifican el objeto original) y devuelven None, salvo pop() e index()/count() que devuelven un valor.
L = [3, 1, 4, 1, 5, 9, 2, 6]
# --- AÑADIR ELEMENTOS ---
L.append(7) # añade 7 al final: [..., 6, 7]
L.insert(0, 0) # inserta 0 en índice 0: [0, 3, 1, ...]
L.extend([8, 5, 3]) # añade varios al final (equivale a +=)
L += [10, 11] # también válido (extend interno)
# --- ELIMINAR ELEMENTOS ---
L.remove(1) # elimina la primera aparición de 1
ultimo = L.pop() # elimina y devuelve el último
tercero = L.pop(2) # elimina y devuelve el elemento en índice 2
del L[0] # elimina el elemento en índice 0
del L[1:3] # elimina porción (slicing)
L.clear() # vacía la lista → []
# --- BUSCAR ---
L = [10, 20, 30, 20, 40]
L.index(20) # 1 ← índice de la primera aparición
L.index(20, 2) # 3 ← busca a partir del índice 2
L.count(20) # 2 ← cuántas veces aparece
# --- REORDENAR ---
L.reverse() # invierte in-place
L.sort() # ordena ascendente in-place
L.sort(reverse=True) # ordena descendente in-place
# --- COPIAR ---
copia = L.copy() # copia superficial (equivale a L[:])
# --- FUNCIONES BUILT-IN (no modifican la lista) ---
len(L) # longitud
sum(L) # suma (si son números)
min(L), max(L) # mínimo y máximo
sorted(L) # nueva lista ordenada (L no cambia)
list(reversed(L)) # nueva lista invertida (L no cambia)
enumerate(L) # genera (índice, valor) para iterar
zip(L1, L2) # empareja elementos de dos listas
🔢 Ordenar listas: sort() vs sorted()
Python ofrece dos formas de ordenar: sort() modifica la lista original, sorted() crea una lista nueva. Ambos aceptan el parámetro key para especificar el criterio de ordenación, y reverse=True para orden descendente.
numeros = [3, 1, 4, 1, 5, 9, 2, 6]
# sort() — modifica in-place, devuelve None
numeros.sort() # [1, 1, 2, 3, 4, 5, 6, 9]
numeros.sort(reverse=True) # [9, 6, 5, 4, 3, 2, 1, 1]
# sorted() — devuelve lista nueva, no modifica el original
ordenada = sorted(numeros) # nueva lista
print(numeros) # sin cambios
# ⚠️ Error clásico: guardar el resultado de sort()
resultado = numeros.sort() # resultado = None ← trampa
resultado = sorted(numeros) # ✅ esto sí funciona
# Ordenar con criterio personalizado (key=)
palabras = ["banana", "manzana", "kiwi", "pera", "fresa"]
# Por longitud
palabras.sort(key=len)
# ['kiwi', 'pera', 'fresa', 'banana', 'manzana']
# Por último carácter
palabras.sort(key=lambda p: p[-1])
# Por segunda letra
palabras.sort(key=lambda p: p[1])
# Ordenar lista de dicts por un campo
personas = [
{"nombre": "Ana", "edad": 28},
{"nombre": "Luis", "edad": 22},
{"nombre": "Marta", "edad": 35},
]
personas.sort(key=lambda p: p["edad"])
# [Luis 22, Ana 28, Marta 35]
# Ordenar por múltiples criterios (primero edad, luego nombre)
personas.sort(key=lambda p: (p["edad"], p["nombre"]))
# sorted() funciona con cualquier iterable
sorted("Python") # ['P', 'h', 'n', 'o', 't', 'y'] (alfabético)
sorted((5, 3, 1, 4, 2)) # [1, 2, 3, 4, 5] (desde tupla)
📋 Copiar listas: la trampa de la referencia
Este es uno de los errores más frecuentes en Python para principiantes. La asignación b = a no copia la lista: hace que b y a apunten al mismo objeto en memoria. Cualquier cambio en uno afecta al otro.
# ❌ Asignación simple — NO es una copia
a = [1, 2, 3]
b = a # b es el mismo objeto que a
b.append(4)
print(a) # [1, 2, 3, 4] ← a también cambió
print(b is a) # True
# ✅ Copia superficial (suficiente para listas de valores simples)
a = [1, 2, 3]
b = a[:] # slicing completo
b = list(a) # constructor list()
b = a.copy() # método copy()
# Los tres son equivalentes para listas planas
b.append(4)
print(a) # [1, 2, 3] ← a no cambia ✅
print(b is a) # False
# ⚠️ Copia superficial NO es suficiente para listas anidadas
a = [[1, 2], [3, 4]]
b = a[:] # copia superficial
b[0].append(99) # modifica la sublista
print(a) # [[1, 2, 99], [3, 4]] ← a también cambió
# ✅ Copia profunda para listas anidadas
import copy
a = [[1, 2], [3, 4]]
b = copy.deepcopy(a)
b[0].append(99)
print(a) # [[1, 2], [3, 4]] ← intacta
a[:] o list(a) para copias simples. Si la lista contiene otras listas, diccionarios u objetos mutables, usa copy.deepcopy(a).
🏗️ Listas anidadas y matrices
Una lista puede contener otras listas, formando estructuras bidimensionales o de mayor profundidad. Son la forma más sencilla de representar matrices y tablas en Python antes de usar NumPy.
# Matriz 3×3
matriz = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
# Acceso: primero fila, luego columna
matriz[0][0] # 1 ← fila 0, columna 0
matriz[1][2] # 6 ← fila 1, columna 2
matriz[2][-1] # 9 ← última columna de la última fila
# Iterar por filas
for fila in matriz:
print(fila)
# Iterar por todos los elementos
for fila in matriz:
for elemento in fila:
print(elemento, end=" ")
# 1 2 3 4 5 6 7 8 9
# Comprensión para aplanar la matriz en una lista
plana = [x for fila in matriz for x in fila]
# [1, 2, 3, 4, 5, 6, 7, 8, 9]
# Transponer una matriz (filas → columnas)
transpuesta = [[fila[i] for fila in matriz] for i in range(len(matriz[0]))]
# [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
# Más elegante con zip:
transpuesta = [list(fila) for fila in zip(*matriz)]
# zip(*matriz) desempaqueta las filas y zip las recombina como columnas
# Crear una matriz NxM de ceros
N, M = 3, 4
# ✅ Forma correcta (cada fila es un objeto diferente)
ceros = [[0] * M for _ in range(N)]
# ❌ Forma incorrecta (todas las filas son el MISMO objeto)
ceros_mal = [[0] * M] * N
ceros_mal[0][0] = 1
print(ceros_mal) # [[1,0,0,0],[1,0,0,0],[1,0,0,0]] ← todas cambian
💡 Patrones idiomáticos con listas
# 1. Comprobar si la lista está vacía
lista = []
if not lista: # Pythonic ✅ (lista vacía es falsy)
print("vacía")
if len(lista) == 0: # también válido pero más verboso
# 2. Obtener el primer / último elemento con seguridad
def primero(L, defecto=None):
return L[0] if L else defecto
def ultimo(L, defecto=None):
return L[-1] if L else defecto
# 3. Eliminar duplicados manteniendo el orden (Python 3.7+)
lista = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3]
sin_dups = list(dict.fromkeys(lista)) # [3, 1, 4, 5, 9, 2, 6]
# 4. Aplanar una lista de listas
from itertools import chain
anidada = [[1, 2], [3, 4], [5]]
plana = list(chain.from_iterable(anidada)) # [1, 2, 3, 4, 5]
# 5. Separar pares e impares (o cualquier partición)
numeros = range(10)
pares = [x for x in numeros if x % 2 == 0]
impares = [x for x in numeros if x % 2 != 0]
# 6. Zip: iterar dos listas en paralelo
nombres = ["Ana", "Luis", "Marta"]
notas = [9.2, 7.5, 8.8]
for nombre, nota in zip(nombres, notas):
print(f"{nombre}: {nota}")
# 7. Enumerate: índice + valor al mismo tiempo
for i, fruta in enumerate(["manzana", "pera", "uva"]):
print(f"{i}: {fruta}")
# 0: manzana 1: pera 2: uva
# 8. Girar / rotar una lista
lista = [1, 2, 3, 4, 5]
n = 2
rotada = lista[n:] + lista[:n] # [3, 4, 5, 1, 2]
# 9. Intercalar dos listas
a = [1, 3, 5]
b = [2, 4, 6]
intercalada = [x for par in zip(a, b) for x in par]
# [1, 2, 3, 4, 5, 6]
# 10. Chunks: dividir en sublistas de tamaño n
def chunks(lst, n):
return [lst[i:i+n] for i in range(0, len(lst), n)]
print(chunks([1,2,3,4,5,6,7], 3)) # [[1,2,3],[4,5,6],[7]]
🛠️ Programa completo: gestor de tareas
"""
Gestor de tareas en línea de comandos.
Demuestra operaciones reales con listas de Python.
"""
tareas = [] # lista principal: cada tarea es un dict
def agregar(texto, prioridad=2):
"""Añade una tarea. Prioridad: 1=alta, 2=normal, 3=baja."""
tareas.append({"texto": texto, "prioridad": prioridad, "hecho": False})
def completar(indice):
"""Marca una tarea como completada."""
if 0 <= indice < len(tareas):
tareas[indice]["hecho"] = True
else:
print(f" Índice {indice} no válido (hay {len(tareas)} tareas).")
def eliminar_completadas():
"""Elimina todas las tareas completadas (modifica la lista en su lugar)."""
tareas[:] = [t for t in tareas if not t["hecho"]]
def listar(mostrar_hechas=True):
"""Lista las tareas ordenadas por prioridad."""
pendientes = [t for t in tareas if not t["hecho"]]
completadas = [t for t in tareas if t["hecho"]]
simbolo_p = {1: "🔴", 2: "🟡", 3: "🟢"}
print(f"\n 📋 TAREAS PENDIENTES ({len(pendientes)})")
for i, t in enumerate(sorted(pendientes, key=lambda x: x["prioridad"])):
idx = tareas.index(t)
print(f" [{idx}] {simbolo_p[t['prioridad']]} {t['texto']}")
if mostrar_hechas and completadas:
print(f"\n ✅ COMPLETADAS ({len(completadas)})")
for t in completadas:
print(f" ✓ {t['texto']}")
def buscar(termino):
"""Devuelve tareas que contienen el término (case-insensitive)."""
termino = termino.lower()
return [t for t in tareas if termino in t["texto"].lower()]
# Cargar datos de ejemplo
agregar("Revisar correo del cliente", prioridad=1)
agregar("Preparar presentación", prioridad=1)
agregar("Actualizar dependencias del proyecto", prioridad=2)
agregar("Comprar café para la oficina", prioridad=3)
agregar("Responder mensajes pendientes", prioridad=2)
# Menú principal
MENU = """
=== GESTOR DE TAREAS ===
1. Listar todas
2. Añadir tarea
3. Completar tarea
4. Buscar
5. Eliminar completadas
6. Salir
"""
while True:
print(MENU)
opcion = input("Opción: ").strip()
if opcion == "1":
listar()
elif opcion == "2":
texto = input(" Texto: ").strip()
p = input(" Prioridad (1=alta, 2=normal, 3=baja) [2]: ").strip() or "2"
agregar(texto, int(p))
print(" Tarea añadida.")
elif opcion == "3":
listar(mostrar_hechas=False)
try:
idx = int(input(" Índice a completar: ").strip())
completar(idx)
except ValueError:
print(" Índice no válido.")
elif opcion == "4":
termino = input(" Buscar: ").strip()
resultado = buscar(termino)
if resultado:
for t in resultado:
estado = "✓" if t["hecho"] else "·"
print(f" [{estado}] {t['texto']}")
else:
print(" Sin resultados.")
elif opcion == "5":
antes = len(tareas)
eliminar_completadas()
print(f" Eliminadas {antes - len(tareas)} tarea(s) completada(s).")
elif opcion == "6":
print("Hasta luego.")
break
🐛 Errores clásicos con listas
1. Modificar una lista mientras la iteras
# ❌ Comportamiento indefinido — omite elementos
lista = [1, -2, 3, -4, 5]
for x in lista:
if x < 0:
lista.remove(x) # modifica la lista durante la iteración
print(lista) # [1, 3, -4, 5] ← ¡-4 no se elimina!
# ✅ Itera sobre una copia o usa comprensión
lista = [x for x in lista if x >= 0] # [1, 3, 5]
2. Guardar el resultado de list.sort()
ordenada = lista.sort() # sort() devuelve None
print(ordenada) # None ← trampa
ordenada = sorted(lista) # sorted() devuelve lista nueva ✅
3. La trampa de [[0]*N]*M
matriz = [[0] * 3] * 3 # 3 referencias al MISMO objeto
matriz[0][0] = 1
print(matriz)
# [[1, 0, 0], [1, 0, 0], [1, 0, 0]] ← todas cambian
# ✅ Usar comprensión para crear filas independientes
matriz = [[0] * 3 for _ in range(3)]
matriz[0][0] = 1
print(matriz)
# [[1, 0, 0], [0, 0, 0], [0, 0, 0]] ✅
4. Confundir append() y extend()
a = [1, 2]
a.append([3, 4]) # añade una lista como UN elemento: [1, 2, [3, 4]]
a.extend([3, 4]) # añade cada elemento: [1, 2, 3, 4]
5. IndexError con lista vacía
lista = []
lista[0] # IndexError: list index out of range
# ✅ Comprobar antes de acceder
if lista:
primero = lista[0]
✅ Resumen y próximos pasos
Las listas son secuencias ordenadas y mutables, la estructura más versátil de Python. Domina el slicing para extraer partes con elegancia, los métodos para modificarlas de forma eficiente, y la distinción entre copias superficiales y profundas para evitar bugs difíciles de detectar.
La siguiente lección explora las tuplas: qué ocurre cuando la inmutabilidad no es una restricción sino una decisión de diseño.
❓ Preguntas frecuentes
❓ Preguntas frecuentes sobre Listas en Python: la estructura de datos más versátil
Las dudas más comunes respondidas de forma clara y directa.
💬 Foro de discusión
¿Tienes dudas sobre Listas en Python: la estructura de datos más versátil? Comparte tu pregunta con la comunidad.
Todavía no hay mensajes. ¡Sé el primero en participar!