Ordenar listas, y los métodos que vienen con ellas
Lo que separa sorted() de .sort(), las respuestas que dan max/min/sum/count/index, cambiar la regla con key=, y convertir una lista en texto legible con join().
¿Cómo quieres aprender?
El problema que vamos a resolver
Las notas de siete personas están en una lista. Hay tres preguntas por responder: ¿cuál es la más alta, cuál es la media y quiénes son las tres primeras?
Con las herramientas del capítulo doce, las dos primeras se pueden hacer con un bucle —laborioso, pero posible—. La tercera es distinta. "Las tres primeras" significa ponerlas en orden primero, y todavía no tenemos forma de ordenar nada.
Estos trabajos salen tan a menudo que Python los trae incorporados. El capítulo de hoy es esa lista de herramientas —y una trampa en la que casi todo el mundo cae al menos una vez: hay dos formas de la misma operación, una que cambia la lista y otra que devuelve una nueva, y elegir la mala no produce ningún error.
Al terminar este capítulo podrás
- Distinguir
sorted()de.sort()y saber cuál quiere cada trabajo - Sacar respuestas de una lista con
max,min,sum,countyindex - Cambiar la regla que usa una ordenación con
key= - Convertir una lista en texto legible con
join() - Explicar por qué ordenar una lista de tipos mixtos lanza un
TypeError
Requisitos previos: Listas — muchos valores bajo un nombre.
sorted() y .sort() — el corazón de este capítulo
Los dos ordenan. La diferencia está en lo que devuelven.
marks = [72, 45, 90, 61]
ordered = sorted(marks)
print(ordered)
print(marks)
marks.sort()
print(marks)[45, 61, 72, 90]
[72, 45, 90, 61]
[45, 61, 72, 90]Lee esas tres líneas de salida con cuidado.
sorted(marks) construyó una lista ordenada nueva y te la devolvió. El marks original no se tocó —la segunda línea es la prueba—.
marks.sort() no construyó nada. Ordenó la lista original, y el orden anterior se fue para siempre.
Vuelve la regla del capítulo doce: un método que cambia la lista no devuelve nada.
marks = [72, 45, 90, 61]
marks = marks.sort()
print(marks)NoneExactamente igual que con append. Con .sort() no debes guardar el resultado; con sorted() sí, o el trabajo se tira.
¿Cuál y cuándo? Si el orden original ya no hace falta, .sort() es más sencillo y más barato. Si la lista original se usa en otro sitio, o no estás seguro, usa sorted() —no le quita nada a nadie—. Ante la duda, sorted().
Orden inverso y respuestas numéricas
marks = [72, 45, 90, 61]
print(sorted(marks, reverse=True))
print(max(marks), min(marks), sum(marks))
print(round(sum(marks) / len(marks), 2))[90, 72, 61, 45]
90 45 268
67.0max, min y sum son funciones y no métodos, así que se escribe max(marks) y no marks.max(). Para la media no hay nada especial: el total entre la cantidad es la media, y round(..., 2) la recorta para enseñarla.
Fíjate en que el bucle acumulador del capítulo diez se ha convertido en una sola línea. Aprenderlo no fue en vano —lo que hace sum es ese bucle, y algún día necesitarás acumular algo para lo que Python no tenga una función ya hecha—.
Contar y buscar
marks = [72, 45, 90, 45, 61]
print(marks.count(45))
print(marks.index(45))
print(marks.index(90))2
1
2count(x) dice cuántas veces aparece x. index(x) dice dónde está el primero —con dos 45 informa del índice 1 y nunca menciona el segundo—.
Y buscar algo que no está detiene el programa:
marks = [72, 45, 90]
print(marks.index(50))ValueError: 50 is not in listIgual que remove. Acostúmbrate a comprobar primero con if 50 in marks:.
Ordenar texto y key=
names = ["rafi", "Bilal", "ahmed"]
print(sorted(names))
print(sorted(names, key=str.lower))['Bilal', 'ahmed', 'rafi']
['ahmed', 'Bilal', 'rafi']La gente ve el primer resultado y asume que Python se equivocó. No es así —ordena por código de carácter, y ahí toda letra mayúscula va antes que toda letra minúscula. Así que Bilal saltó al principio—.
key=str.lower dice: mientras ordenas, trata cada nombre como si estuviera en minúsculas. Los nombres en sí no cambian —Bilal sigue imprimiéndose con su B mayúscula—, solo la comparación usa la forma en minúscula.
key puede ser cualquier regla:
words = ["bag", "pen", "notebook", "ink"]
print(sorted(words, key=len))['bag', 'pen', 'ink', 'notebook']Ordenadas por longitud. Fíjate en que bag, pen e ink los tres tienen tres caracteres y se quedaron en el orden en que ya estaban —Python no altera los empates—. Esa propiedad se llama ordenación estable, y es lo que hace posible ordenar por varios criterios.
Los tipos mixtos no se pueden ordenar
values = [3, "1", 2]
print(sorted(values))TypeError: '<' not supported between instances of 'str' and 'int'Ordenar significa comparar cosas de dos en dos, y el capítulo ocho mostró que un número y un trozo de texto no se pueden comparar con <. El mensaje es literalmente el mismo. Esto pasa habitualmente con listas leídas de un archivo CSV, donde cada número es en realidad texto.
De una lista a texto — join
Imprimir una lista directamente te da corchetes y comillas, que no son para enseñárselo a una persona:
names = ["rafi", "ahmed", "bilal"]
print(", ".join(names))
print(" | ".join(names))rafi, ahmed, bilal
rafi | ahmed | bilalLa primera vez la línea se lee al revés: el texto que une va primero, y la lista va entre los corchetes. La forma de recordarlo: "une estos con esto".
Hay una condición, y es estricta: todos los valores tienen que ser texto.
marks = [72, 45, 90]
print(", ".join(marks))TypeError: sequence item 0: expected str instance, int foundPara unir una lista de números, convierte cada uno en texto primero:
marks = [72, 45, 90]
print(", ".join(str(m) for m in marks))72, 45, 90La parte str(m) for m in marks es nueva, y un capítulo posterior la enseña por completo. De momento, léela así: "por cada m en marks, haz str(m)". Después join une esas cadenas. El ejemplo completo de abajo usa la misma línea.
Un ejemplo completo
report.py:
# A marks report: order them, summarise them, and show the top three
marks = [72, 45, 90, 61, 88, 45, 33]
ordered = sorted(marks, reverse=True)
print("Marks :", marks)
print("Ordered :", ordered)
print("Count :", len(marks))
print("Highest :", max(marks))
print("Lowest :", min(marks))
print("Total :", sum(marks))
print("Average :", round(sum(marks) / len(marks), 2))
print("How many 45s:", marks.count(45))
top_three = ordered[:3]
print("Top three:", top_three)
print("As text :", ", ".join(str(m) for m in top_three))
passed = []
for mark in marks:
if mark >= 40:
passed.append(mark)
print("Passed :", len(passed), "of", len(marks))Marks : [72, 45, 90, 61, 88, 45, 33]
Ordered : [90, 88, 72, 61, 45, 45, 33]
Count : 7
Highest : 90
Lowest : 33
Total : 434
Average : 62.0
How many 45s: 2
Top three: [90, 88, 72]
As text : 90, 88, 72
Passed : 6 of 7Hay dos decisiones de diseño que merece la pena notar.
Se eligió sorted() en vez de .sort() porque el marks original se imprime otra vez en la primera línea. Con .sort(), las líneas "Marks" y "Ordered" habrían mostrado lo mismo y nada se habría quejado. Ese es precisamente el error silencioso con el que empezó este capítulo.
Las tres primeras salieron de un slice —ordered[:3]. No hizo falta ninguna función aparte de "las tres primeras": los tres primeros de una lista descendente son las tres primeras. Cuando algo está ordenado, una cantidad sorprendente de preguntas se convierte en un slice.
Cuando falla
Mi lista se convirtió en None al ordenarla La línea decía marks = marks.sort(). .sort() ordena la lista misma y devuelve None —escribe solo marks.sort(). Para una lista nueva, ordered = sorted(marks).
Ordenar también cambió la lista original Usaste .sort(), que ordena en el sitio. Usa sorted() para dejar la original como estaba.
TypeError: '<' not supported between instances of 'str' and 'int' La lista mezcla números y texto. Ejecuta print(marks) y mira qué valores llevan comillas; si vino de un archivo probablemente todos son texto y necesitan int().
ValueError: 50 is not in list index() o remove() recibieron un valor que la lista no contiene. Comprueba con in primero.
TypeError: sequence item 0: expected str instance, int found join() recibió una lista de números. Envuelve cada uno en str().
Después de ordenar nombres, todos los que empiezan con mayúscula salieron primero Ese es el comportamiento correcto, no un fallo. Para ordenar como una persona espera, sorted(names, key=str.lower).
Paso 4 de 6 — Predice
Comprueba lo que has entendido
Se usó sorted(). ¿Qué imprimen las dos líneas?
marks = [72, 45, 90]
ordered = sorted(marks)
print(ordered)
print(marks)- A[45, 72, 90] [72, 45, 90]
- B[45, 72, 90] None
- C[45, 72, 90] [45, 72, 90]
- D[72, 45, 90] [45, 72, 90]
El resultado de .sort() se guarda de nuevo en marks. ¿Qué se imprime?
marks = [72, 45, 90]
marks = marks.sort()
print(marks)- AUn `AttributeError`
- B[72, 45, 90]
- C[45, 72, 90]
- DNone
Se intenta unir una lista de números con comas. ¿Qué pasa?
marks = [72, 45, 90]
print(", ".join(marks))- AUn `TypeError` — `join` solo puede unir texto
- B724590
- C72, 45, 90
- D[72, 45, 90]
Para responder necesitas una cuenta
Inicia sesión para comprobar tus respuestas
Las preguntas están arriba, y resolverlas mentalmente es lo que de verdad importa. Inicia sesión para ver las respuestas, las explicaciones y las pistas de tres niveles.
Tu turno
Escribe un archivo llamado inventory.py con una lista de al menos siete nombres de productos y una lista de sus precios.
A partir de los precios, imprime:
- El más alto, el más bajo, el total y la media (a dos decimales)
- Una lista ordenada nueva —y luego demuestra que la original no ha cambiado—
- Los tres más caros, usando un slice
A partir de los nombres, imprime:
- Orden alfabético, ignorando mayúsculas
- Ordenado por la longitud del nombre
- Todos los nombres en una sola línea, separados por comas, usando
join
Luego haz un experimento: llama a .sort() sobre los precios e imprime de nuevo las líneas de la parte 1. ¿Qué números cambiaron y cuáles no? Explícate a ti mismo por qué los que no cambiaron no podían cambiar —guárdate esa respuesta y la trampa de este capítulo nunca te pillará—.
Paso 6 de 6
Reto — el quiz del capítulo
Diez preguntas de fácil a difícil. Las últimas son difíciles a propósito.
Inicia sesión para hacer el quiz