Skip to content
This page has been auto-translated and may contain errors.View in English

Listas

Una variable contiene una sola cosa. Una lista contiene muchas cosas en orden, todas bajo un mismo nombre. Una tabla de posiciones es una secuencia ordenada de puntajes. Un cuestionario es una colección de preguntas. Una vez que necesitas administrar un grupo de valores relacionados, necesitas una lista.

Las listas son la secuencia ordenada y mutable de propósito general de Python. Son la opción natural para cualquier cosa que cambie con el tiempo: elementos agregados o eliminados, orden reorganizado, contenidos filtrados u ordenados. Cuando el orden importa y la colección cambia, una lista suele ser la primera opción correcta.

list es el arreglo dinámico de Python: una secuencia ordenada y mutable respaldada por una asignación contigua en el heap. El acceso aleatorio es O(1). append() es O(1) amortizado porque el arreglo sobreasigna y crece al desbordarse. insert() y remove() son O(n) porque desplazan los elementos subsiguientes. Estos costos deberían guiar las decisiones sobre cuándo preferir otras estructuras.

Creando una lista

Corchetes, valores separados por comas. Las listas pueden contener cualquier combinación de tipos, y una lista vacía es válida y común como punto de partida que vas construyendo con el tiempo.

Las listas se definen con sintaxis de corchetes y preservan el orden de inserción. Pueden contener cualquier valor de Python, incluidas otras listas. La lista vacía [] es el punto de partida estándar cuando acumulas elementos de forma incremental.

El literal de corchetes asigna un nuevo objeto list en el heap con cierta capacidad preasignada. Los elementos pueden ser cualquier objeto de Python; la lista almacena referencias, no valores directamente. Los tipos de elementos heterogéneos son válidos pero poco comunes en la práctica fuera de scripts rápidos.

python
scores   = [87, 92, 74, 65, 91]
players  = ["Sofía", "Mateo", "Valentina"]
mixed    = ["Sofía", 87, True, 3.14]   # cualquier tipo, aunque poco común
empty    = []

Indexación y segmentación

Las listas usan la misma numeración que las cadenas: las posiciones empiezan en 0, los números negativos cuentan desde el final. Lees cualquier elemento por su posición. Como las listas son mutables, también puedes escribir en una posición específica.

La indexación y segmentación de listas siguen las mismas reglas que las cadenas. La diferencia clave es la mutabilidad: puedes asignar a un índice o a una segmentación para cambiar elementos en el lugar, algo que las cadenas no permiten.

list.__getitem__ acepta enteros y objetos slice, siguiendo las mismas reglas de delimitación que str. __setitem__ habilita la asignación de elementos. La asignación por segmentación reemplaza un rango de elementos y puede redimensionar la lista si el reemplazo tiene una longitud diferente: lst[1:3] = [10, 20, 30] reemplaza dos elementos por tres.

python
scores = [87, 92, 74, 65, 91]

scores[0]      # 87  (primero)
scores[-1]     # 91  (último)
scores[1:3]    # [92, 74]
scores[:2]     # [87, 92]
scores[::-1]   # [91, 65, 74, 92, 87]  (invertido)

scores[0] = 90   # mutable: funciona (las cadenas lanzarían TypeError)

Agregando elementos

Tres métodos para agregar elementos. append() agrega un solo elemento al final y es lo que usarás casi siempre. insert() agrega en una posición específica. extend() fusiona otra lista.

append() es O(1) amortizado y es la forma estándar de construir una lista elemento por elemento. insert() es O(n) porque desplaza los elementos subsiguientes. extend() es equivalente a += y más eficiente que append() repetido dentro de un bucle.

append() usa el búfer preasignado y solo copia cuando el desbordamiento dispara un redimensionamiento. El factor de crecimiento es de aproximadamente 1.125x después de los primeros redimensionamientos, dando O(1) amortizado. insert(0, x) es O(n): cada elemento se desplaza a la derecha. Para inserciones frecuentes al inicio, collections.deque proporciona appendleft O(1). extend(iterable) llama a __iter__ una vez y crece en una sola operación.

python
scores = [87, 92, 74]

scores.append(65)          # [87, 92, 74, 65]
scores.insert(1, 100)      # [87, 100, 92, 74, 65]
scores.extend([55, 71])    # [87, 100, 92, 74, 65, 55, 71]

Un error común: append() con una lista agrega toda la lista como un solo elemento, dándote una lista dentro de una lista. Usa extend() para fusionarla en su lugar:

append(x) siempre agrega x como un solo elemento. Pasar una lista a append() te da una lista anidada. Usa extend() cuando quieras fusionar todos los elementos de otra lista en esta:

append(x) llama a list_append con x como un único objeto sin importar el tipo. extend(iterable) llama a __iter__ en el argumento y agrega cada elemento individualmente. El operador += llama a __iadd__, que llama a extend internamente.

python
scores.append([55, 71])    # [..., [55, 71]]  lista anidada, probablemente incorrecto
scores.extend([55, 71])    # [..., 55, 71]    fusionado, correcto

Eliminando elementos

Cuatro herramientas para eliminar elementos. remove() busca por valor. pop() elimina por posición y te devuelve el elemento. del elimina por posición sin valor de retorno. clear() vacía toda la lista.

remove() es O(n): escanea la primera ocurrencia por valor. pop() sin argumento es O(1) para el último elemento. pop(i) para cualquier otra posición es O(n) porque los elementos se desplazan. del scores[i] es equivalente a pop(i) pero descarta el valor de retorno.

remove(value) llama a __eq__ en cada elemento hasta encontrar una coincidencia, luego desplaza todos los elementos subsiguientes a la izquierda: O(n). pop(-1) es O(1), no se necesita desplazamiento. pop(i) para cualquier otro índice es O(n). Para eliminaciones frecuentes desde posiciones arbitrarias, considera reestructurar tus datos o usar una colección diferente.

python
scores = [87, 92, 74, 65, 91]

scores.remove(74)    # elimina la primera ocurrencia de 74
scores.pop()         # elimina y devuelve el último elemento (91)
scores.pop(0)        # elimina y devuelve el elemento en la posición 0 (87)
del scores[1]        # elimina en la posición 1, sin valor de retorno
scores.clear()       # elimina todo

remove() lanza un ValueError si el valor no está en la lista. Verifica primero con in si no estás seguro:

python
if 74 in scores:
    scores.remove(74)

remove() lanza ValueError en caso de no encontrar el valor. La verificación con in agrega un escaneo O(n) adicional; haces dos pasadas. Para código de un solo uso esto está bien. El manejo adecuado de errores con try/except ValueError se cubre en el capítulo Archivos y excepciones.

El patrón in + remove() son dos escaneos O(n). Cuando no se necesita preservar el orden, un enfoque más rápido es intercambiar el objetivo con el último elemento y hacer pop: O(1). Para verificar pertenencia a un conjunto con búsqueda O(1), usa set en lugar de list, cubierto en el capítulo Tuplas y conjuntos.

Ordenamiento

sorted() devuelve una lista ordenada nueva y deja la original intacta. .sort() ordena la lista en el lugar y devuelve None. Esa diferencia importa más de lo que parece.

sorted() es la opción segura por defecto: nunca modifica la original. .sort() modifica en el lugar y devuelve None, lo cual es una trampa común. Asignar el resultado de .sort() te da None, no la lista ordenada. Usa sorted() cuando necesites mantener la original intacta; usa .sort() cuando solo quieras la versión ordenada.

Ambos usan Timsort: un ordenamiento híbrido merge/insertion, O(n log n) en el peor caso y O(n) en datos casi ordenados. Timsort es estable: los elementos iguales conservan su orden relativo original. .sort() devuelve None deliberadamente (separación comando-consulta). sorted() acepta cualquier iterable, no solo listas, y siempre devuelve una lista.

python
scores = [87, 42, 96, 55, 71]

ranked = sorted(scores)            # [42, 55, 71, 87, 96] (lista nueva)
scores.sort()                      # ordena en el lugar, devuelve None
scores.sort(reverse=True)          # [96, 87, 71, 55, 42]

result = scores.sort()             # result es None, no la lista ordenada

Operaciones útiles

Python tiene un conjunto de herramientas integradas que funcionan directamente con listas. len(), sum(), min() y max() son las cuatro que usarás constantemente.

Las funciones de secuencia integradas funcionan en cualquier lista. in es un escaneo lineal para listas; si necesitas pruebas de pertenencia rápidas y repetidas, conviértela a un conjunto. .index() lanza ValueError si el valor no se encuentra.

len(), sum(), min(), max() todos llaman a __iter__ y funcionan en cualquier iterable, no solo listas. in es O(n) para listas; para búsqueda O(1), usa set. .index(value) también es O(n). sum() tiene start=0 por defecto y no admite concatenación de cadenas; usa "".join() para cadenas.

python
scores = [87, 92, 74, 65, 91]

len(scores)          # 5
sum(scores)          # 409
min(scores)          # 65
max(scores)          # 92
scores.count(87)     # 1
scores.index(74)     # 2
74 in scores         # True
74 not in scores     # False
scores.copy()        # copia superficial
scores.reverse()     # invierte en el lugar

Iteración

Un bucle for recorre una lista un elemento a la vez. La variable después de for recibe cada elemento en su turno. Cuando también necesitas la posición, enumerate() te da ambas sin un contador manual.

for item in list invoca el iterador de la lista y lo avanza en cada paso. enumerate(iterable, start=0) envuelve el iterador y produce pares (índice, valor). Usar enumerate() es más limpio y menos propenso a errores que mantener una variable contadora.

for invoca iter(list) para obtener un list_iterator, luego llama a next() en él hasta StopIteration. enumerate() envuelve cualquier iterador y produce pares (i, value). El parámetro start desplaza el contador pero no afecta el índice subyacente. El desempaquetado i, item = pair funciona porque enumerate produce tuplas.

python
players = ["Sofía", "Mateo", "Valentina"]

for player in players:
    print(player)

for i, player in enumerate(players, start=1):
    print(f"{i}. {player}")
# 1. Sofía
# 2. Mateo
# 3. Valentina

bucles for y enumerate

for y enumerate() se cubren completamente en el capítulo Flujo de control. Versión corta: for player in players se ejecuta una vez por elemento; enumerate() te da tanto la posición como el valor en cada iteración.

Listas anidadas

Una lista puede contener otras listas. Así es como representas una cuadrícula o una tabla: una lista de filas, donde cada fila es una lista de valores. Dos pares de corchetes acceden a un elemento: el primero elige la fila, el segundo elige la columna.

Las listas anidadas son listas de referencias a listas. Cada lista interna es un objeto independiente. Accede con subíndices encadenados: grid[row][col]. Mutar una lista interna afecta a la lista externa porque esta contiene una referencia al mismo objeto.

Las listas anidadas no son un verdadero arreglo 2D: la lista externa contiene referencias a objetos, y las listas internas pueden tener diferentes longitudes y tipos. El acceso encadena dos llamadas a __getitem__. La copia superficial de una lista anidada copia el contenedor externo pero no las listas internas; las modificaciones a las listas internas afectan a ambas copias.

python
grid = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9],
]

grid[0]       # [1, 2, 3]
grid[1][2]    # 6  (fila 1, columna 2)

Mutabilidad: la trampa

Esto sorprende a casi todos. Asignar una lista a una nueva variable no hace una copia. Ambos nombres apuntan a la misma lista. Cambia uno y cambias el otro. Para obtener una copia independiente, tienes que pedirla explícitamente.

La asignación de listas copia la referencia, no el objeto. Ambos nombres apuntan a la misma lista subyacente. Las mutaciones a través de cualquier nombre afectan los mismos datos. Cuando necesites datos independientes, copia explícitamente con .copy(), list() o una segmentación completa [:].

b = a vincula un segundo nombre al mismo objeto lista. Cualquier mutación a través de b muta el objeto al que a también apunta. .copy() y a[:] crean una copia superficial: un nuevo objeto lista con las mismas referencias a elementos. Para listas planas de valores inmutables esto es seguro; para listas anidadas, los objetos internos siguen siendo compartidos.

python
a = [1, 2, 3]
b = a            # b no es una copia; apunta a la misma lista

b.append(4)
print(a)         # [1, 2, 3, 4]  (cambió: a y b son la misma lista)
python
b = a.copy()    # copia independiente
b = list(a)     # mismo resultado
b = a[:]        # también lo mismo

# Las listas anidadas aún comparten sus objetos internos:
matrix = [[1, 2], [3, 4]]
copy   = matrix.copy()

copy[0].append(99)
print(matrix)   # [[1, 2, 99], [3, 4]]  (la lista interna se compartió)

Para estructuras anidadas donde necesites independencia total, copia cada lista interna manualmente o usa copy.deepcopy() de la biblioteca estándar, cubierto en el capítulo Módulos.

Más métodos

MétodoQué hace
.append(item)Agrega al final
.insert(i, item)Inserta en la posición i
.extend(iterable)Agrega todos los elementos de un iterable
.remove(value)Elimina la primera ocurrencia del valor
.pop(i)Elimina y devuelve el elemento en la posición i (por defecto: último)
.clear()Elimina todos los elementos
.index(value)Posición de la primera ocurrencia
.count(value)Número de ocurrencias
.sort()Ordena en el lugar
.reverse()Invierte en el lugar
.copy()Devuelve una copia superficial

En la práctica

Construyendo un rastreador de puntajes: agregar resultados, ordenarlos e imprimir un resumen.

python
scores = []

scores.append(87)
scores.append(54)
scores.append(92)
scores.append(67)
scores.append(45)

scores.sort(reverse=True)

print(f"Puntajes clasificados: {scores}")
print(f"Más alto: {scores[0]}")
print(f"Más bajo:  {scores[-1]}")
print(f"Promedio: {sum(scores) / len(scores):.1f}")
print(f"Top 3:   {scores[:3]}")

Dos listas paralelas de nombres y puntajes: encuentra al mejor jugador e imprime los resultados clasificados.

python
names  = ["Sofía", "Mateo", "Camila", "Diego"]
scores = [87, 74, 92, 55]

best_score  = max(scores)
best_index  = scores.index(best_score)
best_player = names[best_index]

print(f"Mejor jugador: {best_player} ({best_score})")
print(f"Promedio:    {sum(scores) / len(scores):.1f}")

ranked = sorted(scores, reverse=True)
print(f"Distribución (clasificada): {ranked}")

for i in range(len(ranked)):
    print(f"  Rango {i + 1}: {ranked[i]}")

Demostrando la diferencia entre aliasing y copia, y entre copias superficiales y profundas de listas anidadas.

python
# Aliasing: b no es una copia
a = [1, 2, 3]
b = a
b.append(4)
print(a)    # [1, 2, 3, 4]  (mismo objeto)

# Copia superficial: la lista externa es independiente, las listas internas se comparten
matrix    = [[1, 2, 3], [4, 5, 6]]
shallow   = matrix.copy()
shallow[0].append(99)
print(matrix)    # [[1, 2, 3, 99], [4, 5, 6]]  (lista interna compartida)

# Copia profunda manual con un bucle for (no se necesitan imports)
matrix    = [[1, 2, 3], [4, 5, 6]]
deep_copy = []
for row in matrix:
    deep_copy.append(row[:])    # copia cada lista interna explícitamente

deep_copy[0].append(99)
print(matrix)    # [[1, 2, 3], [4, 5, 6]]  (sin cambios)