nick sargento
Tengo una lista de objetos de Python que quiero ordenar por un atributo específico de cada objeto:
[Tag(name="toe", count=10), Tag(name="leg", count=2), ...]
¿Cómo ordeno la lista por .count
¿en orden descendente?
Bancos de Kenan
Para ordenar la lista en su lugar:
orig_list.sort(key=lambda x: x.count, reverse=True)
Para devolver una nueva lista, utilice sorted
:
new_list = sorted(orig_list, key=lambda x: x.count, reverse=True)
Explicación:
key=lambda x: x.count
ordena por conteo.reverse=True
clasifica en orden descendente.
Más en clasificación por claves.
-
Ningún problema. por cierto, si muhuk tiene razón y es una lista de objetos de Django, debería considerar su solución. Sin embargo, para el caso general de ordenar objetos, mi solución probablemente sea la mejor práctica.
– Kenan Banks
31 de diciembre de 2008 a las 17:12
-
En listas grandes, obtendrá un mejor rendimiento utilizando operator.attrgetter(‘count’) como clave. Esta es solo una forma optimizada (nivel inferior) de la función lambda en esta respuesta.
– David Eyk
31 de diciembre de 2008 a las 19:35
-
Gracias por la gran respuesta. En caso de que sea una lista de diccionarios y ‘contar’ sea una de sus claves, entonces debe cambiarse como se muestra a continuación: ut.sort(key=lambda x: x[‘count’]inversa=Verdadero)
– dganesh2002
8 dic 2016 a las 21:20
-
Supongo que merece la siguiente actualización: si es necesario ordenar por varios campos, podría lograrse mediante llamadas consecutivas a sort(), porque Python está usando un algoritmo de ordenación estable.
– uuu777
23 de febrero de 2020 a las 14:41
-
Gracias @KenanBanks, tenías razón. De manera molesta, Outlook estaba haciendo algunas cosas raras con las zonas horarias del calendario, por lo que algunas llegaron sin los detalles de la zona horaria… ¡no tengo idea de por qué!
– petysmith
20 de abril de 2022 a las 6:05
tzot
Una forma que puede ser más rápida, especialmente si su lista tiene muchos registros, es usar operator.attrgetter("count")
. Sin embargo, esto podría ejecutarse en una versión previa al operador de Python, por lo que sería bueno tener un mecanismo de respaldo. Es posible que desee hacer lo siguiente, entonces:
try: import operator
except ImportError: keyfun= lambda x: x.count # use a lambda if no operator module
else: keyfun= operator.attrgetter("count") # use operator since it's faster than lambda
ut.sort(key=keyfun, reverse=True) # sort in-place
-
Aquí usaría el nombre de variable “keyfun” en lugar de “cmpfun” para evitar confusiones. El método sort() también acepta una función de comparación a través del argumento cmp=.
– akaihola
2 de enero de 2009 a las 12:16
-
Esto no parece funcionar si el objeto tiene atributos agregados dinámicamente (si lo ha hecho
self.__dict__ = {'some':'dict'}
después de la__init__
método). Aunque no sé por qué debería ser diferente.– tutuca
07/01/2013 a las 20:40
-
@tutuca: nunca he reemplazado la instancia
__dict__
. Tenga en cuenta que “un objeto que tiene atributos agregados dinámicamente” y “establecer los atributos de un objeto__dict__
atributo” son conceptos casi ortogonales. Lo digo porque su comentario parece implicar que establecer el__dict__
El atributo es un requisito para agregar atributos dinámicamente.– tzot
9 de enero de 2013 a las 23:14
-
@tzot: si entiendo el uso de
operator.attrgetter
podría proporcionar una función con cualquier nombre de propiedad y devolver una colección ordenada.– Resumen
24 de febrero de 2016 a las 18:16
-
Para aquellos que buscan más información: wiki.python.org/moin/HowTo/Sorting#Operator_Module_Functions
– alxs
20 de enero de 2017 a las 15:01
José Vidal
Los lectores deben notar que el método key=:
ut.sort(key=lambda x: x.count, reverse=True)
es muchas veces más rápido que agregar operadores de comparación enriquecidos a los objetos. Me sorprendió leer esto (página 485 de “Python in a Nutshell”). Puede confirmar esto ejecutando pruebas en este pequeño programa:
#!/usr/bin/env python
import random
class C:
def __init__(self,count):
self.count = count
def __cmp__(self,other):
return cmp(self.count,other.count)
longList = [C(random.random()) for i in xrange(1000000)] #about 6.1 secs
longList2 = longList[:]
longList.sort() #about 52 - 6.1 = 46 secs
longList2.sort(key = lambda c: c.count) #about 9 - 6.1 = 3 secs
Mis pruebas, muy mínimas, muestran que el primer tipo es más de 10 veces más lento, pero el libro dice que es solo unas 5 veces más lento en general. La razón por la que dicen se debe al algoritmo de clasificación altamente optimizado utilizado en python (timsort).
Aún así, es muy extraño que .sort(lambda) sea más rápido que el antiguo .sort(). Espero que arreglen eso.
-
Definición
__cmp__
es equivalente a llamar.sort(cmp=lambda)
no.sort(key=lambda)
por lo que no es extraño en absoluto.– tzot
9 de septiembre de 2019 a las 7:46
-
@tzot tiene toda la razón. El primer tipo tiene que comparar objetos entre sí una y otra vez. La segunda ordenación accede a cada objeto solo una vez para extraer su valor de conteo y luego realiza una ordenación numérica simple que está altamente optimizada. Una comparación más justa sería
longList2.sort(cmp = cmp)
. Probé esto y funcionó casi igual que.sort()
. (Además: tenga en cuenta que el parámetro de clasificación “cmp” se eliminó en Python 3).–Bryan Roach
29 de octubre de 2019 a las 4:56
-
Enfoque orientado a objetos
Es una buena práctica hacer que la lógica de clasificación de objetos, si corresponde, sea una propiedad de la clase en lugar de incorporarla en cada instancia en la que se requiera la clasificación.
Esto garantiza la coherencia y elimina la necesidad de un código repetitivo.
Como mínimo, debe especificar __eq__
y __lt__
operaciones para que esto funcione. Entonces solo usa sorted(list_of_objects)
.
class Card(object):
def __init__(self, rank, suit):
self.rank = rank
self.suit = suit
def __eq__(self, other):
return self.rank == other.rank and self.suit == other.suit
def __lt__(self, other):
return self.rank < other.rank
hand = [Card(10, 'H'), Card(2, 'h'), Card(12, 'h'), Card(13, 'h'), Card(14, 'h')]
hand_order = [c.rank for c in hand] # [10, 2, 12, 13, 14]
hand_sorted = sorted(hand)
hand_sorted_order = [c.rank for c in hand_sorted] # [2, 10, 12, 13, 14]
from operator import attrgetter
ut.sort(key = attrgetter('count'), reverse = True)
muhuk
Se parece mucho a una lista de instancias del modelo Django ORM.
¿Por qué no ordenarlos en una consulta como esta?
ut = Tag.objects.order_by('-count')
robar
Agregue operadores de comparación enriquecidos a la clase de objeto, luego use el método sort() de la lista.
Ver rica comparación en python.
Actualizar: Aunque este método funcionaría, creo que la solución de Triptych se adapta mejor a su caso porque es mucho más simple.
Duplicado: stackoverflow.com/questions/157424/…, stackoverflow.com/questions/222752/…, stackoverflow.com/questions/327191/…
– S. Lott
31 de diciembre de 2008 a las 18:23
Clasificación CÓMO para aquellos que buscan más información sobre la clasificación en Python.
– Jeyekomon
30 de mayo de 2018 a las 8:48
además de operator.attrgetter(‘attribute_name’) también puede usar funtores como clave como object_list.sort(key=my_sorting_functor(‘my_key’)), omitiendo la implementación intencionalmente.
– vijay shanker
08/04/2019 a las 20:07