Listas y matrices
En esta guía (15)
Hasta ahora cada variable guardaba un solo valor. En esta lección vas a usar listas, la colección más usada de Python: una secuencia ordenada de valores que podés recorrer, modificar, ordenar y filtrar. También vas a armar matrices (listas de listas), escribir tus primeras list comprehensions y entender una trampa clásica: la diferencia entre copiar una lista y tener dos nombres para la misma.
Crear una lista
Una lista se escribe entre corchetes, con los elementos separados por comas:
temperaturas = [18.5, 21.0, 19.2, 24.8, 22.1]
dias = ["lunes", "martes", "miércoles"]
vacia = []
mezcla = [1, "dos", 3.0, True]
print(temperaturas)
print(len(dias))
[18.5, 21.0, 19.2, 24.8, 22.1]
3
Una lista puede tener elementos de cualquier tipo, incluso mezclados, aunque en la práctica casi siempre guarda elementos del mismo tipo. len() devuelve la cantidad de elementos.
Índices
Cada elemento tiene una posición, llamada índice, que empieza en 0:
frutas = ["manzana", "pera", "banana", "kiwi"]
# 0 1 2 3
# -4 -3 -2 -1
print(frutas[0]) # manzana
print(frutas[2]) # banana
print(frutas[-1]) # kiwi (el último)
print(frutas[-2]) # banana (el anteúltimo)
Los índices negativos cuentan desde el final: -1 es el último elemento, sin importar cuántos haya.
Las listas son mutables: podés cambiar un elemento asignando a su índice.
frutas = ["manzana", "pera", "banana", "kiwi"]
frutas[1] = "durazno"
print(frutas) # ['manzana', 'durazno', 'banana', 'kiwi']
Si pedís un índice que no existe, da error:
>>> frutas[10]
IndexError: list index out of range
Slicing: rebanadas de una lista
Con lista[inicio:fin] obtenés una nueva lista con los elementos desde inicio hasta fin, sin incluir fin (igual que range):
numeros = [10, 20, 30, 40, 50, 60]
print(numeros[1:4]) # [20, 30, 40]
print(numeros[:3]) # [10, 20, 30] (desde el principio)
print(numeros[3:]) # [40, 50, 60] (hasta el final)
print(numeros[-2:]) # [50, 60] (los dos últimos)
print(numeros[::2]) # [10, 30, 50] (de 2 en 2)
print(numeros[::-1]) # [60, 50, 40, 30, 20, 10] (invertida)
El tercer valor es el paso, como en range. A diferencia de los índices sueltos, el slicing no da error si te pasás del largo: numeros[4:100] devuelve [50, 60].
Preguntar si algo está
El operador in devuelve True si el elemento está en la lista:
invitados = ["Ana", "Bruno", "Carla"]
print("Bruno" in invitados) # True
print("Diego" in invitados) # False
print("Diego" not in invitados) # True
Métodos para modificar listas
Los métodos son funciones que se llaman sobre un valor con un punto: lista.metodo(...). Estos son los más usados:
compras = ["pan", "leche"]
compras.append("queso") # agrega al final
print(compras) # ['pan', 'leche', 'queso']
compras.insert(0, "café") # agrega en una posición
print(compras) # ['café', 'pan', 'leche', 'queso']
compras.extend(["arroz", "sal"]) # agrega varios al final
print(compras) # ['café', 'pan', 'leche', 'queso', 'arroz', 'sal']
compras.remove("leche") # quita la primera aparición de un valor
print(compras) # ['café', 'pan', 'queso', 'arroz', 'sal']
ultimo = compras.pop() # quita y devuelve el último
print(ultimo, compras) # sal ['café', 'pan', 'queso', 'arroz']
primero = compras.pop(0) # quita y devuelve el de esa posición
print(primero, compras) # café ['pan', 'queso', 'arroz']
Y para buscar y contar:
notas = [7, 9, 4, 9, 6]
print(notas.index(9)) # 1 (posición de la primera aparición)
print(notas.count(9)) # 2 (cuántas veces aparece)
remove e index dan ValueError si el valor no está. Si no estás seguro, preguntá antes con in.
Ordenar
Hay dos formas, y la diferencia importa:
numeros = [5, 2, 9, 1]
ordenada = sorted(numeros) # devuelve una lista NUEVA ordenada
print(ordenada) # [1, 2, 5, 9]
print(numeros) # [5, 2, 9, 1] (la original no cambió)
numeros.sort() # ordena la MISMA lista, no devuelve nada
print(numeros) # [1, 2, 5, 9]
numeros.sort(reverse=True) # de mayor a menor
print(numeros) # [9, 5, 2, 1]
numeros.reverse() # invierte el orden actual
print(numeros) # [1, 2, 5, 9]
sorted(lista)es una función: deja la original intacta y te da una nueva.lista.sort()es un método: modifica la lista y devuelveNone.
Las listas de strings se ordenan alfabéticamente (con las mayúsculas antes que las minúsculas).
Funciones de resumen
ventas = [1200, 850, 2300, 640]
print(sum(ventas)) # 4990
print(max(ventas)) # 2300
print(min(ventas)) # 640
print(sum(ventas) / len(ventas)) # 1247.5 (promedio)
sum, max y min reciben la lista entera. Con una lista vacía, max y min dan error, y el promedio dividiría por cero: chequeá antes que no esté vacía.
Recorrer una lista
Con for recorrés los elementos directamente, sin índices:
precios = [1500, 320, 980]
total = 0
for precio in precios:
total += precio
print(total) # 2800
Si además necesitás la posición, usá enumerate, que te da el índice y el elemento en cada vuelta:
tareas = ["lavar", "cocinar", "estudiar"]
for i, tarea in enumerate(tareas, start=1):
print(f"{i}. {tarea}")
1. lavar
2. cocinar
3. estudiar
Un patrón muy común es construir una lista nueva recorriendo otra: arrancás con una lista vacía y agregás con append.
precios = [1500, 320, 980, 2100]
caros = []
for precio in precios:
if precio > 1000:
caros.append(precio)
print(caros) # [1500, 2100]
List comprehensions
Ese patrón de "lista vacía + for + append" es tan común que Python tiene una sintaxis compacta para escribirlo, la list comprehension:
precios = [1500, 320, 980, 2100]
caros = [p for p in precios if p > 1000]
con_iva = [p * 1.21 for p in precios]
caros_con_iva = [round(p * 1.21) for p in precios if p > 1000]
print(caros) # [1500, 2100]
print(con_iva) # [1815.0, 387.2, 1185.8, 2541.0]
print(caros_con_iva) # [1815, 2541]
Se lee así: [expresión for elemento in lista if condición], "la expresión, para cada elemento de la lista, si se cumple la condición". El if es opcional.
Con range también funciona:
cuadrados = [n ** 2 for n in range(1, 6)]
print(cuadrados) # [1, 4, 9, 16, 25]
Cuándo usarlas: cuando querés armar una lista nueva transformando o filtrando otra, y la lógica entra cómoda en una línea. Cuándo no: si necesitás cortar el recorrido con break, hacer varias cosas por elemento o la línea se vuelve difícil de leer. Ahí, un for común es más claro.
Listas de listas: matrices
Un elemento de una lista puede ser otra lista. Así se representa una matriz (una tabla de filas y columnas):
# 3 filas x 4 columnas: ventas por sucursal (fila) y trimestre (columna)
ventas = [
[120, 135, 150, 160],
[ 90, 95, 110, 100],
[200, 210, 190, 230],
]
print(ventas[0]) # [120, 135, 150, 160] (fila 0)
print(ventas[2][3]) # 230 (fila 2, columna 3)
print(len(ventas)) # 3 filas
print(len(ventas[0])) # 4 columnas
Para recorrer todos los valores se anidan dos for:
ventas = [
[120, 135, 150, 160],
[ 90, 95, 110, 100],
[200, 210, 190, 230],
]
for i, fila in enumerate(ventas):
total_fila = 0
for valor in fila:
total_fila += valor
print(f"Sucursal {i}: {total_fila}")
Sucursal 0: 565
Sucursal 1: 395
Sucursal 2: 830
El for de afuera recorre las filas; el de adentro, los valores de cada fila. (Podrías usar sum(fila), pero conviene ver el recorrido explícito una vez.)
Para totalizar por columna, recorrés con índices:
ventas = [
[120, 135, 150, 160],
[ 90, 95, 110, 100],
[200, 210, 190, 230],
]
for col in range(len(ventas[0])):
total_col = 0
for fila in ventas:
total_col += fila[col]
print(f"Trimestre {col + 1}: {total_col}")
Trimestre 1: 410
Trimestre 2: 440
Trimestre 3: 450
Trimestre 4: 490
Copiar vs. referenciar
Esta es la trampa más famosa de las listas:
original = [1, 2, 3]
otra = original
otra.append(4)
print(original) # [1, 2, 3, 4]
otra = original no copia la lista: crea un segundo nombre que apunta a la misma lista. Modificar una es modificar la otra. Para tener una copia independiente:
original = [1, 2, 3]
copia = original.copy() # también sirve list(original) u original[:]
copia.append(4)
print(original) # [1, 2, 3]
print(copia) # [1, 2, 3, 4]
Con matrices hay que tener un cuidado extra: .copy() copia la lista de afuera, pero las filas siguen siendo compartidas. Y hay otra trampa al crear una matriz con *:
mal = [[0] * 3] * 2 # ¡las dos filas son la MISMA lista!
mal[0][0] = 9
print(mal) # [[9, 0, 0], [9, 0, 0]]
bien = [[0] * 3 for _ in range(2)] # cada fila es una lista nueva
bien[0][0] = 9
print(bien) # [[9, 0, 0], [0, 0, 0]]
_ es un nombre de variable válido que, por convención, significa "no me importa este valor".
Por qué pasa esto: las variables en Python son nombres que apuntan a valores. Los números y los strings no se pueden modificar (son inmutables), así que compartirlos nunca causa sorpresas. Las listas sí se pueden modificar (son mutables), y ahí importa si dos nombres apuntan a la misma.
Errores comunes
- Olvidar que el índice empieza en 0. El primer elemento es
lista[0]y el último eslista[len(lista) - 1], o más simple,lista[-1]. IndexError: list index out of range: pediste una posición que no existe. Revisá el largo conlen().- Asignar el resultado de
sort().numeros = numeros.sort()dejanumerosenNone. Usánumeros.sort()solo, onumeros = sorted(numeros). - Creer que
b = acopia. Son dos nombres para la misma lista. Usáa.copy(). - Modificar una lista mientras la recorrés (por ejemplo, hacer
removedentro de unforsobre la misma lista): se saltean elementos. Armá una lista nueva con los que querés conservar. - Armar una lista con
for+appendcuando ya hay una herramienta. Para sumar usásum; para contar un valor,count; para filtrar, una comprehension.
Practicá
- Dada
temps = [22.5, 25.1, 19.8, 30.2, 27.4, 18.9, 24.0](temperaturas de una semana), mostrá la máxima, la mínima, el promedio con un decimal y cuántos días superaron el promedio. - Dada la lista
codigos = ["A12", "B07", "A33", "C01", "A05"], armá con una comprehension la lista de los códigos que empiezan con"A". Pista:codigo[0]es la primera letra. - Dada la matriz
m = [[1, 2, 3], [4, 5, 6], [7, 8, 9]], calculá la suma de la diagonal principal (1 + 5 + 9) y armá la transpuesta (filas convertidas en columnas):[[1, 4, 7], [2, 5, 8], [3, 6, 9]]. - Escribí un programa que pida números al usuario hasta que ingrese una línea vacía, los guarde en una lista y al final muestre la lista ordenada de menor a mayor y sin modificar la original.
Soluciones
Ejercicio 1:
temps = [22.5, 25.1, 19.8, 30.2, 27.4, 18.9, 24.0]
promedio = sum(temps) / len(temps)
sobre_promedio = len([t for t in temps if t > promedio])
print(f"Máxima: {max(temps)}")
print(f"Mínima: {min(temps)}")
print(f"Promedio: {promedio:.1f}")
print(f"Días sobre el promedio: {sobre_promedio}")
Máxima: 30.2
Mínima: 18.9
Promedio: 24.0
Días sobre el promedio: 4
Ejercicio 2:
codigos = ["A12", "B07", "A33", "C01", "A05"]
con_a = [c for c in codigos if c[0] == "A"]
print(con_a) # ['A12', 'A33', 'A05']
Ejercicio 3:
m = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
diagonal = 0
for i in range(len(m)):
diagonal += m[i][i]
print(diagonal) # 15
transpuesta = []
for col in range(len(m[0])):
nueva_fila = [fila[col] for fila in m]
transpuesta.append(nueva_fila)
print(transpuesta) # [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
En la diagonal principal, el índice de fila y el de columna coinciden: m[0][0], m[1][1], m[2][2].
Ejercicio 4:
numeros = []
texto = input("Número (Enter para terminar): ")
while texto != "":
numeros.append(float(texto))
texto = input("Número (Enter para terminar): ")
print(f"Ingresados: {numeros}")
print(f"Ordenados: {sorted(numeros)}")
Número (Enter para terminar): 8
Número (Enter para terminar): 3.5
Número (Enter para terminar): 10
Número (Enter para terminar):
Ingresados: [8.0, 3.5, 10.0]
Ordenados: [3.5, 8.0, 10.0]
sorted deja numeros en el orden en que se ingresó; numeros.sort() lo hubiera cambiado.
Resumen
- Una lista es una secuencia ordenada y mutable:
[1, 2, 3]. Los índices empiezan en 0 y los negativos cuentan desde el final. lista[inicio:fin:paso]devuelve una porción nueva, sin incluirfin.append,insert,extend,removeypopmodifican la lista;indexycountbuscan.sorted()devuelve una lista nueva ordenada;.sort()ordena la misma lista y devuelveNone.for elemento in listarecorre;enumerateagrega la posición.[expr for x in lista if cond]arma una lista nueva transformando y filtrando en una línea.- Una matriz es una lista de listas:
m[fila][columna]. b = ano copia: los dos nombres apuntan a la misma lista. Para copiar,a.copy().