Módulo 1 · Lección 24 de 59
Ordenar y buscar en listas
Ordenar en el sitio o en una lista nueva, cambiar el criterio de orden, localizar elementos y resumir una lista con una función.
Una lista llega casi siempre en el orden en que se recogieron los datos, que rara vez es el orden en que quieres mostrarlos. Ordenar alfabéticamente un listado de nombres, poner los precios de menor a mayor o sacar la nota más alta son operaciones tan frecuentes que Python las trae resueltas.
Lo que sí tienes que decidir cada vez es si quieres reordenar la lista que tienes o obtener una lista nueva ordenada dejando la original en paz. Esa decisión es la que separa sort() de sorted(), y confundirlas es el error clásico de esta lección.
sort() ordena la lista en el sitio#
precios = [29.99, 189.00, 15.50, 45.00]
precios.sort()
print(precios)[15.5, 29.99, 45.0, 189.0]La lista original ha cambiado. No hay ninguna lista nueva, y sort() no devuelve nada:
precios = [29.99, 189.00, 15.50]
resultado = precios.sort()
print(resultado)
print(precios)None
[15.5, 29.99, 189.0]Con cadenas ordena alfabéticamente, pero con una salvedad que conviene ver:
nombres = ["marta", "Ana", "luis", "Álvaro"]
nombres.sort()
print(nombres)['Ana', 'luis', 'marta', 'Álvaro']Las mayúsculas van antes que las minúsculas y las letras acentuadas se van al final, porque la comparación usa el código interno de cada carácter, no el orden del diccionario español. Se arregla con el parámetro key, unos apartados más abajo.
sorted() devuelve una lista nueva#
precios = [29.99, 189.00, 15.50, 45.00]
ordenados = sorted(precios)
print(f"nueva: {ordenados}")
print(f"original: {precios}")nueva: [15.5, 29.99, 45.0, 189.0]
original: [29.99, 189.0, 15.5, 45.0]sorted() es una función, no un método, así que se escribe con la lista dentro de los paréntesis. La original queda intacta, y eso lo hace la opción más segura cuando solo quieres mostrar los datos ordenados.
lista.sort() | sorted(lista) | |
|---|---|---|
| Qué es | Método de las listas | Función general |
| A la original | La reordena | No la toca |
| Devuelve | None | Una lista nueva |
| Se escribe | lista.sort() | nueva = sorted(lista) |
| Funciona con | Solo listas | Cualquier colección |
Ese último punto importa: sorted() acepta cadenas, tuplas, conjuntos y diccionarios, y siempre te devuelve una lista.
print(sorted("python"))
print(sorted((3, 1, 2)))['h', 'n', 'o', 'p', 't', 'y']
[1, 2, 3]De mayor a menor: reverse#
Los dos aceptan el parámetro reverse, que da la vuelta al criterio:
puntuaciones = [1200, 850, 3400, 2100]
print(sorted(puntuaciones, reverse=True))
puntuaciones.sort(reverse=True)
print(puntuaciones)[3400, 2100, 1200, 850]
[3400, 2100, 1200, 850]Es lo que quieres para cualquier ranking: la puntuación más alta primero.
Cambiar el criterio: key#
Por defecto se ordena comparando los elementos entre sí. Con key le dices a Python qué mirar de cada elemento para compararlo. Se le pasa el nombre de una función, sin paréntesis.
Ordenar por longitud en vez de alfabéticamente:
palabras = ["murciélago", "sol", "ventana", "pez"]
print(sorted(palabras, key=len))['sol', 'pez', 'ventana', 'murciélago']Lo que ha pasado es que Python ha aplicado len a cada palabra y ha ordenado por el número resultante: 3, 3, 7, 10. "sol" y "pez" empatan a tres letras, y en caso de empate se mantiene el orden que tenían en la lista original. Eso se llama orden estable y es una garantía de Python, no una casualidad.
Ordenar sin que las mayúsculas molesten:
nombres = ["marta", "Ana", "luis", "Pedro"]
print(sorted(nombres)) # las mayúsculas van primero
print(sorted(nombres, key=str.lower)) # orden alfabético de verdad['Ana', 'Pedro', 'luis', 'marta']
['Ana', 'luis', 'marta', 'Pedro']str.lower es el método lower que ya usabas, escrito en la forma "el método de la clase cadena", que es como se le pasa a key. Python lo aplica a cada elemento para decidir el orden, pero los elementos de la lista no se modifican: siguen con sus mayúsculas.
Se pueden combinar key y reverse:
palabras = ["sol", "murciélago", "ventana", "pez"]
print(sorted(palabras, key=len, reverse=True))['murciélago', 'ventana', 'sol', 'pez']Dar la vuelta: reverse()#
No confundas el método reverse() con el parámetro reverse=True. El método invierte el orden actual de la lista, esté ordenada o no, y también trabaja en el sitio devolviendo None:
historial = ["inicio", "productos", "carrito", "pago"]
historial.reverse()
print(historial)['pago', 'carrito', 'productos', 'inicio']Si lo que quieres es una lista nueva invertida, tienes el rebanado [::-1], que no toca la original:
historial = ["inicio", "productos", "carrito", "pago"]
print(historial[::-1])
print(historial)['pago', 'carrito', 'productos', 'inicio']
['inicio', 'productos', 'carrito', 'pago']Buscar: index y count#
index te dice en qué posición está un valor, y count cuántas veces aparece:
colores = ["rojo", "verde", "azul", "verde", "verde"]
print(colores.index("azul"))
print(colores.index("verde")) # solo la primera aparición
print(colores.count("verde"))
print(colores.count("morado")) # no está: cero2
1
3
0Resumir una lista con una función#
Estas cinco funciones convierten una lista entera en un solo valor, y te ahorran el bucle que escribiste a mano en la lección anterior:
temperaturas = [18.2, 24.5, 31.0, 27.8, 15.4]
print(f"suma: {sum(temperaturas)}")
print(f"mínima: {min(temperaturas)}")
print(f"máxima: {max(temperaturas)}")
print(f"media: {sum(temperaturas) / len(temperaturas):.2f}")suma: 116.9
mínima: 15.4
máxima: 31.0
media: 23.38La media no tiene función propia: se calcula dividiendo la suma entre el número de elementos, y conviene darle formato con :.2f porque el resultado suele traer una cola de decimales innecesaria.
min y max también aceptan key, lo que los hace mucho más útiles:
palabras = ["sol", "murciélago", "ventana", "pez"]
print(min(palabras)) # alfabéticamente
print(max(palabras, key=len)) # la más largamurciélago
murciélagoY luego hay dos que trabajan con condiciones, any y all:
notas = [7.5, 4.0, 9.25, 6.0]
comprobaciones = []
for nota in notas:
comprobaciones.append(nota >= 5)
print(comprobaciones)
print(any(comprobaciones)) # ¿alguno es True?
print(all(comprobaciones)) # ¿todos son True?[True, False, True, True]
True
Falseany responde "¿hay al menos uno?" y all responde "¿lo cumplen todos?". Hay al menos un aprobado, pero no han aprobado todos. En la próxima lección verás cómo escribir esa lista de comprobaciones en una sola línea, y entonces any y all se leerán como una frase.
Tabla de referencia#
| Operación | Modifica | Devuelve |
|---|---|---|
lista.sort() | Sí | None |
sorted(lista) | No | Lista nueva |
lista.reverse() | Sí | None |
lista[::-1] | No | Lista nueva |
lista.index(x) | No | Posición, o ValueError |
lista.count(x) | No | Número de apariciones |
sum, min, max | No | Un número |
any, all | No | True o False |
Un ejemplo completo#
jugadores = ["Marta", "ana", "Luis", "pedro", "Sara"]
puntos = [3400, 1200, 2100, 850, 2100]
print("Clasificación")
print("=" * 25)
for posicion, puntuacion in enumerate(sorted(puntos, reverse=True), start=1):
jugador = jugadores[puntos.index(puntuacion)]
print(f"{posicion}. {jugador:<8} {puntuacion:>6}")
print("=" * 25)
print(f"Jugadores: {len(jugadores)}")
print(f"Total: {sum(puntos)}")
print(f"Mejor: {max(puntos)}")
print(f"Peor: {min(puntos)}")
print(f"Media: {sum(puntos) / len(puntos):.1f}")
print(f"Por nombre: {sorted(jugadores, key=str.lower)}")Clasificación
=========================
1. Marta 3400
2. Luis 2100
3. Luis 2100
4. ana 1200
5. pedro 850
=========================
Jugadores: 5
Total: 9650
Mejor: 3400
Peor: 850
Media: 1930.0
Por nombre: ['ana', 'Luis', 'Marta', 'pedro', 'Sara']Mira la clasificación con cuidado: Sara aparece como Luis. El fallo es mío y es a propósito, para que veas el límite de este enfoque. Como Luis y Sara empatan a 2100 puntos, puntos.index(2100) devuelve siempre la primera aparición, así que Sara nunca sale. Mantener dos listas emparejadas y cruzarlas por el valor es frágil; la solución buena es guardar cada jugador con sus puntos en una sola estructura, y eso son los diccionarios de dentro de unas lecciones.